@playcanvas/attribute-parser 1.1.0 → 1.3.0
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/package.json +1 -1
- package/src/index.js +18 -6
- package/src/parsers/attribute-parser.js +11 -20
- package/src/parsers/script-parser.js +4 -3
- package/src/utils/ts-utils.js +34 -4
- package/test/fixtures/asset.invalid.ts +24 -0
- package/test/fixtures/asset.valid.ts +21 -0
- package/test/fixtures/enum.valid.js +5 -21
- package/test/fixtures/enum.valid.ts +49 -0
- package/test/fixtures/json.valid.ts +72 -0
- package/test/tests/valid/asset.test.js +51 -42
- package/test/tests/valid/checkbox.test.js +49 -41
- package/test/tests/valid/enum.test.js +63 -58
- package/test/tests/valid/json.test.js +129 -1
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -8,9 +8,10 @@ import { createDefaultMapFromCDN, flatMapAnyNodes, getExportedNodes, getType, in
|
|
|
8
8
|
const toLowerCamelCase = str => str[0].toLowerCase() + str.substring(1);
|
|
9
9
|
|
|
10
10
|
const COMPILER_OPTIONS = {
|
|
11
|
+
noLib: true,
|
|
11
12
|
strict: false,
|
|
12
13
|
skipLibCheck: true, // Skip type checking of declaration files
|
|
13
|
-
target: ts.ScriptTarget.
|
|
14
|
+
target: ts.ScriptTarget.ES2023, // If this version changes, the types must be updated in the /rollup.config.mjs
|
|
14
15
|
module: ts.ModuleKind.CommonJS,
|
|
15
16
|
checkJs: true, // Enable JSDoc parsing
|
|
16
17
|
allowJs: true,
|
|
@@ -36,19 +37,30 @@ export class JSDocParser {
|
|
|
36
37
|
/**
|
|
37
38
|
* Initializes the JSDocParser with the standard library files
|
|
38
39
|
*
|
|
39
|
-
* @param {string} libPath -
|
|
40
|
+
* @param {string} libPath - A path to a directory of library types, or a path to the '.d.ts' file itself
|
|
40
41
|
* @returns {Promise<JSDocParser>} - The initialized JSDocParser
|
|
41
42
|
*/
|
|
42
|
-
async init(libPath
|
|
43
|
+
async init(libPath) {
|
|
43
44
|
if (this._env) {
|
|
44
45
|
return this;
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
let fsMap;
|
|
48
|
-
if (libPath) {
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
if (!libPath) {
|
|
50
|
+
|
|
51
|
+
// This is a node only option. If no lib path is passed, attempt to resolve ES types from node_modules.
|
|
51
52
|
fsMap = await createDefaultMapFromNodeModules(COMPILER_OPTIONS, ts);
|
|
53
|
+
|
|
54
|
+
} else if (libPath.endsWith('.d.ts')) {
|
|
55
|
+
|
|
56
|
+
// If the libPath is a '.d.ts' file then load it and add it
|
|
57
|
+
const types = await fetch(libPath).then(r => r.text());
|
|
58
|
+
fsMap = new Map([['/lib.d.ts', types]]);
|
|
59
|
+
|
|
60
|
+
} else {
|
|
61
|
+
|
|
62
|
+
// A libPath was supplied, but not to a '.d.ts', so assume this is a path to types
|
|
63
|
+
fsMap = await createDefaultMapFromCDN(COMPILER_OPTIONS, libPath, ts);
|
|
52
64
|
}
|
|
53
65
|
|
|
54
66
|
// Set up the virtual file system and environment
|
|
@@ -4,7 +4,7 @@ import * as ts from 'typescript';
|
|
|
4
4
|
import { ParsingError } from './parsing-error.js';
|
|
5
5
|
import { hasTag } from '../utils/attribute-utils.js';
|
|
6
6
|
import { parseTag, validateTag } from '../utils/tag-utils.js';
|
|
7
|
-
import { extractTextFromDocNode, getLeadingBlockCommentRanges, getType } from '../utils/ts-utils.js';
|
|
7
|
+
import { extractTextFromDocNode, getLeadingBlockCommentRanges, getLiteralValue, getType } from '../utils/ts-utils.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* A class to parse JSDoc comments and extract attribute metadata.
|
|
@@ -167,7 +167,7 @@ export class AttributeParser {
|
|
|
167
167
|
let value = null;
|
|
168
168
|
|
|
169
169
|
// we don't need to serialize the value for arrays
|
|
170
|
-
const serializer = !array && this.typeSerializerMap.get(
|
|
170
|
+
const serializer = !array && this.typeSerializerMap.get(typeName);
|
|
171
171
|
if (serializer) {
|
|
172
172
|
try {
|
|
173
173
|
value = serializer(node.initializer ?? node, this.typeChecker);
|
|
@@ -192,8 +192,8 @@ export class AttributeParser {
|
|
|
192
192
|
let members = [];
|
|
193
193
|
|
|
194
194
|
// Check if there's a type annotation directly on the variable declaration
|
|
195
|
-
if (
|
|
196
|
-
typeNode = node
|
|
195
|
+
if (node.type) {
|
|
196
|
+
typeNode = node;
|
|
197
197
|
} else {
|
|
198
198
|
// Check for JSDoc annotations
|
|
199
199
|
const jsDocs = ts.getJSDocTags(node);
|
|
@@ -203,10 +203,13 @@ export class AttributeParser {
|
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
|
|
206
|
+
// Also consider the elementType
|
|
207
|
+
const type = typeNode && (typeNode.type.elementType ?? typeNode.type);
|
|
208
|
+
|
|
209
|
+
if (typeNode && ts.isTypeReferenceNode(type)) {
|
|
207
210
|
|
|
208
211
|
// resolve the symbol of the type
|
|
209
|
-
let symbol = this.typeChecker.getSymbolAtLocation(
|
|
212
|
+
let symbol = this.typeChecker.getSymbolAtLocation(type.typeName);
|
|
210
213
|
|
|
211
214
|
// Resolve aliases, which are common with imports
|
|
212
215
|
if (symbol && symbol.flags & ts.SymbolFlags.Alias) {
|
|
@@ -222,7 +225,7 @@ export class AttributeParser {
|
|
|
222
225
|
|
|
223
226
|
// Check if the declaration is a TypeScript enum
|
|
224
227
|
if (ts.isEnumDeclaration(declaration)) {
|
|
225
|
-
members = declaration.members.map(member => member.name.getText());
|
|
228
|
+
members = declaration.members.map(member => ({ [member.name.getText()]: member.initializer.text }));
|
|
226
229
|
}
|
|
227
230
|
|
|
228
231
|
// Additionally check for JSDoc enum tag
|
|
@@ -256,19 +259,7 @@ export class AttributeParser {
|
|
|
256
259
|
|
|
257
260
|
if (ts.isPropertyAssignment(property)) {
|
|
258
261
|
const name = property.name && ts.isIdentifier(property.name) && property.name.text;
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
const node = property.initializer;
|
|
262
|
-
|
|
263
|
-
// Enums can only contain primitives (string|number|boolean)
|
|
264
|
-
if (ts.isNumericLiteral(node)) {
|
|
265
|
-
value = parseFloat(node.getText());
|
|
266
|
-
} else if (node.kind === ts.SyntaxKind.TrueKeyword || node.kind === ts.SyntaxKind.FalseKeyword) {
|
|
267
|
-
value = node.kind === ts.SyntaxKind.TrueKeyword;
|
|
268
|
-
} else {
|
|
269
|
-
value = node.getText();
|
|
270
|
-
}
|
|
271
|
-
|
|
262
|
+
const value = getLiteralValue(property.initializer, this.typeChecker);
|
|
272
263
|
members.push({ [name]: value });
|
|
273
264
|
}
|
|
274
265
|
});
|
|
@@ -147,7 +147,8 @@ const SUPPORTED_BLOCK_TAGS = new Map([
|
|
|
147
147
|
['precision', 'number'],
|
|
148
148
|
['size', 'number'],
|
|
149
149
|
['step', 'number'],
|
|
150
|
-
['title', 'string']
|
|
150
|
+
['title', 'string'],
|
|
151
|
+
['default', 'any']
|
|
151
152
|
]);
|
|
152
153
|
|
|
153
154
|
/**
|
|
@@ -195,7 +196,7 @@ const mapAttributesToOutput = (attribute) => {
|
|
|
195
196
|
}
|
|
196
197
|
|
|
197
198
|
// set the default value
|
|
198
|
-
if (attribute.value !== undefined) attribute.default = attribute.value;
|
|
199
|
+
if (attribute.value !== undefined) attribute.default = attribute.default ?? attribute.value;
|
|
199
200
|
|
|
200
201
|
// Curve Attributes specifically should not expose a default value if it's an empty array
|
|
201
202
|
if (attribute.type === 'curve' && Array.isArray(attribute.value) && attribute.value.length === 0) {
|
|
@@ -251,7 +252,7 @@ export class ScriptParser {
|
|
|
251
252
|
const typeName = this.typeChecker.typeToString(type);
|
|
252
253
|
const serializer = SUPPORTED_INITIALIZABLE_TYPE_NAMES.get(typeName);
|
|
253
254
|
if (serializer) {
|
|
254
|
-
this.typeSerializerMap.set(
|
|
255
|
+
this.typeSerializerMap.set(typeName, serializer);
|
|
255
256
|
}
|
|
256
257
|
});
|
|
257
258
|
|
package/src/utils/ts-utils.js
CHANGED
|
@@ -208,7 +208,7 @@ function getSuperClasses(node, typeChecker) {
|
|
|
208
208
|
export function getJSDocCommentRanges(node, text, typeChecker) {
|
|
209
209
|
const commentRanges = [];
|
|
210
210
|
|
|
211
|
-
if (ts.isClassDeclaration(node)) {
|
|
211
|
+
if (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) {
|
|
212
212
|
// get an array of the class an all parent classed
|
|
213
213
|
const heritageChain = getSuperClasses(node, typeChecker);
|
|
214
214
|
|
|
@@ -216,7 +216,7 @@ export function getJSDocCommentRanges(node, text, typeChecker) {
|
|
|
216
216
|
heritageChain.forEach((classNode) => {
|
|
217
217
|
// for each class iterate over it's class members
|
|
218
218
|
classNode.members.forEach((member) => {
|
|
219
|
-
if (ts.isPropertyDeclaration(member) || ts.isSetAccessor(member)) {
|
|
219
|
+
if (ts.isPropertyDeclaration(member) || ts.isSetAccessor(member) || ts.isPropertySignature(member)) {
|
|
220
220
|
const memberName = member.name && ts.isIdentifier(member.name) ? member.name.text : 'unnamed';
|
|
221
221
|
const ranges = getLeadingBlockCommentRanges(member, text);
|
|
222
222
|
if (ranges.length > 0) {
|
|
@@ -310,6 +310,31 @@ export function isEnum(node) {
|
|
|
310
310
|
return false;
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
+
/**
|
|
314
|
+
* Determines the primitive type for enums, or falls back to the actual type name.
|
|
315
|
+
*
|
|
316
|
+
* @param {ts.Type} type - The type to inspect.
|
|
317
|
+
* @param {ts.TypeChecker} typeChecker - The TypeScript type checker.
|
|
318
|
+
* @returns {'string' | 'boolean' | 'number' | null} - The primitive type of the enum or the type's name.
|
|
319
|
+
*/
|
|
320
|
+
export function getPrimitiveEnumType(type, typeChecker) {
|
|
321
|
+
// Check if the type is an enum type
|
|
322
|
+
if (!type.symbol?.declarations?.some(decl => ts.isEnumDeclaration(decl))) return null;
|
|
323
|
+
|
|
324
|
+
// Get the type of enum members
|
|
325
|
+
const enumMembers = type.symbol.declarations[0].members;
|
|
326
|
+
const firstMemberValue = typeChecker.getConstantValue(enumMembers[0]);
|
|
327
|
+
|
|
328
|
+
const validEnumType = [
|
|
329
|
+
'number',
|
|
330
|
+
'string',
|
|
331
|
+
'boolean'
|
|
332
|
+
];
|
|
333
|
+
|
|
334
|
+
const typeOf = typeof firstMemberValue;
|
|
335
|
+
return validEnumType.includes(typeOf) ? typeOf : null;
|
|
336
|
+
}
|
|
337
|
+
|
|
313
338
|
/**
|
|
314
339
|
* Gets the inferred type of a TypeScript node.
|
|
315
340
|
*
|
|
@@ -322,7 +347,7 @@ export function getType(node, typeChecker) {
|
|
|
322
347
|
const type = typeChecker.getTypeAtLocation(node);
|
|
323
348
|
const array = typeChecker.isArrayType(type);
|
|
324
349
|
const actualType = array ? typeChecker.getElementTypeOfArrayType(type) : type;
|
|
325
|
-
const name = typeChecker.typeToString(actualType);
|
|
350
|
+
const name = getPrimitiveEnumType(actualType, typeChecker) ?? typeChecker.typeToString(actualType);
|
|
326
351
|
|
|
327
352
|
return { type: actualType, name, array };
|
|
328
353
|
}
|
|
@@ -489,10 +514,15 @@ const resolvePropertyAccess = (node, typeChecker) => {
|
|
|
489
514
|
if (ts.isPropertyAssignment(declaration) && declaration.initializer) {
|
|
490
515
|
return getLiteralValue(declaration.initializer, typeChecker);
|
|
491
516
|
}
|
|
517
|
+
|
|
492
518
|
if (ts.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
493
519
|
return getLiteralValue(declaration.initializer, typeChecker);
|
|
494
520
|
}
|
|
495
|
-
|
|
521
|
+
|
|
522
|
+
if (ts.isEnumMember(declaration)) {
|
|
523
|
+
return declaration.initializer ? getLiteralValue(declaration.initializer, typeChecker) : declaration.name.getText();
|
|
524
|
+
}
|
|
525
|
+
|
|
496
526
|
}
|
|
497
527
|
}
|
|
498
528
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// eslint-disable-next-line
|
|
2
|
+
import { Script, Asset } from 'playcanvas';
|
|
3
|
+
|
|
4
|
+
class Example extends Script {
|
|
5
|
+
/**
|
|
6
|
+
* @attribute
|
|
7
|
+
* @resource nothing
|
|
8
|
+
*/
|
|
9
|
+
a: Asset;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @attribute
|
|
13
|
+
* @resource 1
|
|
14
|
+
*/
|
|
15
|
+
b : Asset;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @attribute
|
|
19
|
+
* @resource
|
|
20
|
+
*/
|
|
21
|
+
c : Asset;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export { Example };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// eslint-disable-next-line
|
|
2
|
+
import { Script, Asset } from 'playcanvas';
|
|
3
|
+
|
|
4
|
+
class Example extends Script {
|
|
5
|
+
/** @attribute */
|
|
6
|
+
a : Asset;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @attribute
|
|
10
|
+
* @resource texture
|
|
11
|
+
*/
|
|
12
|
+
b : Asset;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @attribute
|
|
16
|
+
* @resource container
|
|
17
|
+
*/
|
|
18
|
+
c : Asset[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { Example };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { Script
|
|
1
|
+
import { Script } from 'playcanvas';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @enum {number}
|
|
5
5
|
*/
|
|
6
6
|
const NumberEnum = {
|
|
7
|
-
A:
|
|
8
|
-
B:
|
|
9
|
-
C:
|
|
7
|
+
A: 13,
|
|
8
|
+
B: 14,
|
|
9
|
+
C: 23
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -19,16 +19,6 @@ const StringEnum = {
|
|
|
19
19
|
C: 'c'
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
/**
|
|
23
|
-
* @enum {Vec3}
|
|
24
|
-
*/
|
|
25
|
-
// eslint-disable-next-line
|
|
26
|
-
const Vec3Enum = {
|
|
27
|
-
A: new Vec3(1, 2, 3),
|
|
28
|
-
B: new Vec3(4, 5, 6),
|
|
29
|
-
C: new Vec3(7, 8, 9)
|
|
30
|
-
};
|
|
31
|
-
|
|
32
22
|
/**
|
|
33
23
|
* @enum {NumberEnum}
|
|
34
24
|
*/
|
|
@@ -65,17 +55,11 @@ class Example extends Script {
|
|
|
65
55
|
*/
|
|
66
56
|
h;
|
|
67
57
|
|
|
68
|
-
/**
|
|
69
|
-
* @attribute
|
|
70
|
-
* @type {Vec3Enum}
|
|
71
|
-
*/
|
|
72
|
-
i;
|
|
73
|
-
|
|
74
58
|
/**
|
|
75
59
|
* @attribute
|
|
76
60
|
* @type {NumberNumberEnum}
|
|
77
61
|
*/
|
|
78
|
-
|
|
62
|
+
i = 2;
|
|
79
63
|
}
|
|
80
64
|
|
|
81
65
|
export { Example };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Script, Vec3 } from 'playcanvas';
|
|
2
|
+
|
|
3
|
+
enum NumberEnum {
|
|
4
|
+
A = 13,
|
|
5
|
+
B = 14,
|
|
6
|
+
C = 23
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
enum StringEnum {
|
|
10
|
+
A = 'a',
|
|
11
|
+
B = 'b',
|
|
12
|
+
C = 'c'
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
enum NumberNumberEnum {
|
|
16
|
+
A = NumberEnum.A,
|
|
17
|
+
B = NumberEnum.B,
|
|
18
|
+
C = NumberEnum.C
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
class Example extends Script {
|
|
22
|
+
/**
|
|
23
|
+
* @attribute
|
|
24
|
+
*/
|
|
25
|
+
e : NumberEnum = NumberEnum.A;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @attribute
|
|
29
|
+
*/
|
|
30
|
+
f : NumberEnum = 1;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @attribute
|
|
34
|
+
* @size 2
|
|
35
|
+
*/
|
|
36
|
+
g : NumberEnum[];
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @attribute
|
|
40
|
+
*/
|
|
41
|
+
h : StringEnum;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @attribute
|
|
45
|
+
*/
|
|
46
|
+
i : NumberNumberEnum = 2;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { Example };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Script, Vec3 } from 'playcanvas';
|
|
2
|
+
|
|
3
|
+
interface Folder {
|
|
4
|
+
/**
|
|
5
|
+
* @attribute
|
|
6
|
+
*/
|
|
7
|
+
a: boolean;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @attribute
|
|
11
|
+
* @default 10
|
|
12
|
+
*/
|
|
13
|
+
b: number;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @attribute
|
|
17
|
+
* @default hello
|
|
18
|
+
*/
|
|
19
|
+
c : string;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @attribute
|
|
23
|
+
*/
|
|
24
|
+
d : Vec3[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface NestedFolder {
|
|
28
|
+
/**
|
|
29
|
+
* @attribute
|
|
30
|
+
*/
|
|
31
|
+
x : Folder;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @interface
|
|
36
|
+
*/
|
|
37
|
+
class Example extends Script {
|
|
38
|
+
/**
|
|
39
|
+
* @attribute
|
|
40
|
+
*/
|
|
41
|
+
f : Folder;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @attribute
|
|
45
|
+
* @size 2
|
|
46
|
+
*/
|
|
47
|
+
g : Folder[];
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @attribute
|
|
51
|
+
*/
|
|
52
|
+
h : NestedFolder;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @attribute
|
|
56
|
+
*/
|
|
57
|
+
i = { a: true, b: 10, c: 'hello', d: [new Vec3(1, 2, 3)] };
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @attribute
|
|
61
|
+
*/
|
|
62
|
+
l : { x: number, y: number };
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @attribute
|
|
66
|
+
*/
|
|
67
|
+
m : Example;
|
|
68
|
+
|
|
69
|
+
n = 10;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export { Example };
|
|
@@ -3,48 +3,57 @@ import { describe, it, before } from 'mocha';
|
|
|
3
3
|
|
|
4
4
|
import { parseAttributes } from '../../utils.js';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
6
|
+
function runTests(fileName) {
|
|
7
|
+
|
|
8
|
+
const isTS = fileName.endsWith('.ts');
|
|
9
|
+
const script = isTS ? 'TS' : 'JS';
|
|
10
|
+
|
|
11
|
+
describe(`${script}: VALID: Asset attribute`, function () {
|
|
12
|
+
let data;
|
|
13
|
+
before(async function () {
|
|
14
|
+
data = await parseAttributes(fileName);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('only results should exist', function () {
|
|
18
|
+
expect(data).to.exist;
|
|
19
|
+
expect(data[0]).to.not.be.empty;
|
|
20
|
+
expect(data[1]).to.be.empty;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('Example: should exist without errors', function () {
|
|
24
|
+
expect(data[0]?.example).to.exist;
|
|
25
|
+
expect(data[0].example.attributes).to.not.be.empty;
|
|
26
|
+
expect(data[0].example.errors).to.be.empty;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('a: should be a asset attribute', function () {
|
|
30
|
+
expect(data[0].example.attributes.a).to.exist;
|
|
31
|
+
expect(data[0].example.attributes.a.name).to.equal('a');
|
|
32
|
+
expect(data[0].example.attributes.a.type).to.equal('asset');
|
|
33
|
+
expect(data[0].example.attributes.a.array).to.equal(false);
|
|
34
|
+
expect(data[0].example.attributes.a.default).to.equal(null);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('b: should be a asset attribute with a texture resource', function () {
|
|
38
|
+
expect(data[0].example.attributes.b).to.exist;
|
|
39
|
+
expect(data[0].example.attributes.b.name).to.equal('b');
|
|
40
|
+
expect(data[0].example.attributes.b.type).to.equal('asset');
|
|
41
|
+
expect(data[0].example.attributes.b.array).to.equal(false);
|
|
42
|
+
expect(data[0].example.attributes.b.default).to.equal(null);
|
|
43
|
+
expect(data[0].example.attributes.b.resource).to.equal('texture');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('c: should be a asset attribute array with a container resource', function () {
|
|
47
|
+
expect(data[0].example.attributes.c).to.exist;
|
|
48
|
+
expect(data[0].example.attributes.c.name).to.equal('c');
|
|
49
|
+
expect(data[0].example.attributes.c.type).to.equal('asset');
|
|
50
|
+
expect(data[0].example.attributes.c.array).to.equal(true);
|
|
51
|
+
expect(data[0].example.attributes.c.default).to.equal(null);
|
|
52
|
+
expect(data[0].example.attributes.c.resource).to.equal('container');
|
|
53
|
+
});
|
|
40
54
|
|
|
41
|
-
it('c: should be a asset attribute array with a container resource', function () {
|
|
42
|
-
expect(data[0].example.attributes.c).to.exist;
|
|
43
|
-
expect(data[0].example.attributes.c.name).to.equal('c');
|
|
44
|
-
expect(data[0].example.attributes.c.type).to.equal('asset');
|
|
45
|
-
expect(data[0].example.attributes.c.array).to.equal(true);
|
|
46
|
-
expect(data[0].example.attributes.c.default).to.equal(null);
|
|
47
|
-
expect(data[0].example.attributes.c.resource).to.equal('container');
|
|
48
55
|
});
|
|
56
|
+
}
|
|
49
57
|
|
|
50
|
-
|
|
58
|
+
runTests('./asset.valid.js');
|
|
59
|
+
runTests('./asset.valid.ts');
|
|
@@ -3,47 +3,55 @@ import { describe, it, before } from 'mocha';
|
|
|
3
3
|
|
|
4
4
|
import { parseAttributes } from '../../utils.js';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
6
|
+
function runTests(fileName) {
|
|
7
|
+
|
|
8
|
+
const isTS = fileName.endsWith('.ts');
|
|
9
|
+
const script = isTS ? 'TS' : 'JS';
|
|
10
|
+
|
|
11
|
+
describe(`${script}: VALID: Checkbox attribute`, function () {
|
|
12
|
+
let data;
|
|
13
|
+
before(async function () {
|
|
14
|
+
data = await parseAttributes(fileName);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('only results should exist', function () {
|
|
18
|
+
expect(data).to.exist;
|
|
19
|
+
expect(data[0]).to.not.be.empty;
|
|
20
|
+
expect(data[1]).to.be.empty;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('Example: should exist without errors', function () {
|
|
24
|
+
expect(data[0]?.example).to.exist;
|
|
25
|
+
expect(data[0].example.attributes).to.not.be.empty;
|
|
26
|
+
expect(data[0].example.errors).to.be.empty;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('a: should be a checkbox attribute', function () {
|
|
30
|
+
expect(data[0].example.attributes.a).to.exist;
|
|
31
|
+
expect(data[0].example.attributes.a.name).to.equal('a');
|
|
32
|
+
expect(data[0].example.attributes.a.type).to.equal('boolean');
|
|
33
|
+
expect(data[0].example.attributes.a.array).to.equal(false);
|
|
34
|
+
expect(data[0].example.attributes.a.default).to.equal(false);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('b: should be a checkbox attribute with a default value', function () {
|
|
38
|
+
expect(data[0].example.attributes.b).to.exist;
|
|
39
|
+
expect(data[0].example.attributes.b.name).to.equal('b');
|
|
40
|
+
expect(data[0].example.attributes.b.type).to.equal('boolean');
|
|
41
|
+
expect(data[0].example.attributes.b.array).to.equal(false);
|
|
42
|
+
expect(data[0].example.attributes.b.default).to.equal(true);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('c: should be a checkbox attribute array with a size', function () {
|
|
46
|
+
expect(data[0].example.attributes.c).to.exist;
|
|
47
|
+
expect(data[0].example.attributes.c.name).to.equal('c');
|
|
48
|
+
expect(data[0].example.attributes.c.type).to.equal('boolean');
|
|
49
|
+
expect(data[0].example.attributes.c.array).to.equal(true);
|
|
50
|
+
expect(data[0].example.attributes.c.size).to.equal(2);
|
|
51
|
+
expect(data[0].example.attributes.c.default).equal(null);
|
|
52
|
+
});
|
|
39
53
|
|
|
40
|
-
it('c: should be a checkbox attribute array with a size', function () {
|
|
41
|
-
expect(data[0].example.attributes.c).to.exist;
|
|
42
|
-
expect(data[0].example.attributes.c.name).to.equal('c');
|
|
43
|
-
expect(data[0].example.attributes.c.type).to.equal('boolean');
|
|
44
|
-
expect(data[0].example.attributes.c.array).to.equal(true);
|
|
45
|
-
expect(data[0].example.attributes.c.size).to.equal(2);
|
|
46
|
-
expect(data[0].example.attributes.c.default).equal(null);
|
|
47
54
|
});
|
|
55
|
+
}
|
|
48
56
|
|
|
49
|
-
|
|
57
|
+
runTests('./checkbox.valid.js');
|
|
@@ -3,71 +3,76 @@ import { describe, it, before } from 'mocha';
|
|
|
3
3
|
|
|
4
4
|
import { parseAttributes } from '../../utils.js';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
let data;
|
|
8
|
-
before(async function () {
|
|
9
|
-
data = await parseAttributes('./enum.valid.js');
|
|
10
|
-
});
|
|
6
|
+
function runTests(fileName) {
|
|
11
7
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
expect(data[0]).to.not.be.empty;
|
|
15
|
-
expect(data[1]).to.be.empty;
|
|
16
|
-
});
|
|
8
|
+
const isTS = fileName.endsWith('.ts');
|
|
9
|
+
const script = isTS ? 'TS' : 'JS';
|
|
17
10
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
11
|
+
describe(`${script}: VALID: Enum attribute`, function () {
|
|
12
|
+
let data;
|
|
13
|
+
before(async function () {
|
|
14
|
+
data = await parseAttributes(fileName);
|
|
15
|
+
});
|
|
23
16
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
expect(data[0].example.attributes.e.default).to.equal(0);
|
|
30
|
-
});
|
|
17
|
+
it('only results should exist', function () {
|
|
18
|
+
expect(data).to.exist;
|
|
19
|
+
expect(data[0]).to.not.be.empty;
|
|
20
|
+
expect(data[1]).to.be.empty;
|
|
21
|
+
});
|
|
31
22
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
expect(data[0].example.attributes.f.default).to.equal(1);
|
|
38
|
-
});
|
|
23
|
+
it('Example: should exist without errors', function () {
|
|
24
|
+
expect(data[0]?.example).to.exist;
|
|
25
|
+
expect(data[0].example.errors).to.be.empty;
|
|
26
|
+
expect(data[0].example.attributes).to.not.be.empty;
|
|
27
|
+
});
|
|
39
28
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
});
|
|
29
|
+
it('e: should be a enum attribute', function () {
|
|
30
|
+
expect(data[0].example.attributes.e).to.exist;
|
|
31
|
+
expect(data[0].example.attributes.e.name).to.equal('e');
|
|
32
|
+
expect(data[0].example.attributes.e.type).to.equal('number');
|
|
33
|
+
expect(data[0].example.attributes.e.array).to.equal(false);
|
|
34
|
+
expect(data[0].example.attributes.e.default).to.equal(13);
|
|
35
|
+
});
|
|
48
36
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
37
|
+
it('f: should be a enum attribute with a default value', function () {
|
|
38
|
+
expect(data[0].example.attributes.f).to.exist;
|
|
39
|
+
expect(data[0].example.attributes.f.name).to.equal('f');
|
|
40
|
+
expect(data[0].example.attributes.f.type).to.equal('number');
|
|
41
|
+
expect(data[0].example.attributes.f.array).to.equal(false);
|
|
42
|
+
expect(data[0].example.attributes.f.default).to.equal(1);
|
|
43
|
+
});
|
|
56
44
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
45
|
+
it('g: should be a enum attribute array with a size', function () {
|
|
46
|
+
expect(data[0].example.attributes.g).to.exist;
|
|
47
|
+
expect(data[0].example.attributes.g.name).to.equal('g');
|
|
48
|
+
expect(data[0].example.attributes.g.type).to.equal('number');
|
|
49
|
+
expect(data[0].example.attributes.g.array).to.equal(true);
|
|
50
|
+
expect(data[0].example.attributes.g.size).to.equal(2);
|
|
51
|
+
expect(data[0].example.attributes.g.default).equal(null);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('h: should be a enum attribute', function () {
|
|
55
|
+
expect(data[0].example.attributes.h).to.exist;
|
|
56
|
+
expect(data[0].example.attributes.h.name).to.equal('h');
|
|
57
|
+
expect(data[0].example.attributes.h.type).to.equal('string');
|
|
58
|
+
expect(data[0].example.attributes.h.array).to.equal(false);
|
|
59
|
+
expect(data[0].example.attributes.h.enum).to.be.an('array').with.lengthOf(3);
|
|
60
|
+
expect(data[0].example.attributes.h.enum[0]).to.deep.equal({ A: 'a' });
|
|
61
|
+
expect(data[0].example.attributes.h.enum[1]).to.deep.equal({ B: 'b' });
|
|
62
|
+
expect(data[0].example.attributes.h.enum[2]).to.deep.equal({ C: 'c' });
|
|
63
|
+
expect(data[0].example.attributes.h.default).to.equal('');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('j: should be a enum attribute array with a size', function () {
|
|
67
|
+
expect(data[0].example.attributes.i).to.exist;
|
|
68
|
+
expect(data[0].example.attributes.i.name).to.equal('i');
|
|
69
|
+
expect(data[0].example.attributes.i.type).to.equal('number');
|
|
70
|
+
expect(data[0].example.attributes.i.array).to.equal(false);
|
|
71
|
+
expect(data[0].example.attributes.i.default).equal(2);
|
|
72
|
+
});
|
|
64
73
|
|
|
65
|
-
it('j: should be a enum attribute array with a size', function () {
|
|
66
|
-
expect(data[0].example.attributes.j).to.exist;
|
|
67
|
-
expect(data[0].example.attributes.j.name).to.equal('j');
|
|
68
|
-
expect(data[0].example.attributes.j.type).to.equal('number');
|
|
69
|
-
expect(data[0].example.attributes.j.array).to.equal(false);
|
|
70
|
-
expect(data[0].example.attributes.j.default).equal(2);
|
|
71
74
|
});
|
|
75
|
+
}
|
|
72
76
|
|
|
73
|
-
|
|
77
|
+
runTests('./enum.valid.js');
|
|
78
|
+
runTests('./enum.valid.ts');
|
|
@@ -3,7 +3,8 @@ import { describe, it, before } from 'mocha';
|
|
|
3
3
|
|
|
4
4
|
import { parseAttributes } from '../../utils.js';
|
|
5
5
|
|
|
6
|
-
describe('VALID:
|
|
6
|
+
describe('JS: VALID: Asset attribute', function () {
|
|
7
|
+
|
|
7
8
|
let data;
|
|
8
9
|
before(async function () {
|
|
9
10
|
data = await parseAttributes('./json.valid.js');
|
|
@@ -147,3 +148,130 @@ describe('VALID: JSON attribute', function () {
|
|
|
147
148
|
expect(data[0].example.attributes.o).to.not.exist;
|
|
148
149
|
});
|
|
149
150
|
});
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
describe('TS: VALID: Asset attribute', function () {
|
|
154
|
+
|
|
155
|
+
let data;
|
|
156
|
+
before(async function () {
|
|
157
|
+
data = await parseAttributes('./json.valid.ts');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('only results should exist', function () {
|
|
161
|
+
expect(data).to.exist;
|
|
162
|
+
expect(data[0]).to.not.be.empty;
|
|
163
|
+
expect(data[1]).to.be.empty;
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('Example: should exist without errors', function () {
|
|
167
|
+
expect(data[0]?.example).to.exist;
|
|
168
|
+
expect(data[0].example.attributes).to.not.be.empty;
|
|
169
|
+
expect(data[0].example.errors).to.be.empty;
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('f: should be a json attribute', function () {
|
|
173
|
+
expect(data[0].example.attributes.f).to.exist;
|
|
174
|
+
expect(data[0].example.attributes.f.name).to.equal('f');
|
|
175
|
+
expect(data[0].example.attributes.f.type).to.equal('json');
|
|
176
|
+
expect(data[0].example.attributes.f.array).to.equal(false);
|
|
177
|
+
expect(data[0].example.attributes.f.default).to.equal(null);
|
|
178
|
+
|
|
179
|
+
expect(data[0].example.attributes.f.schema).to.exist;
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('f[a]: should be a boolean attribute', function () {
|
|
183
|
+
expect(data[0].example.attributes.f.schema[0].name).to.equal('a');
|
|
184
|
+
expect(data[0].example.attributes.f.schema[0].type).to.equal('boolean');
|
|
185
|
+
expect(data[0].example.attributes.f.schema[0].array).to.equal(false);
|
|
186
|
+
expect(data[0].example.attributes.f.schema[0].default).to.equal(false);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('f[b]: should be a numeric attribute', function () {
|
|
190
|
+
expect(data[0].example.attributes.f.schema[1].name).to.equal('b');
|
|
191
|
+
expect(data[0].example.attributes.f.schema[1].type).to.equal('number');
|
|
192
|
+
expect(data[0].example.attributes.f.schema[1].array).to.equal(false);
|
|
193
|
+
expect(data[0].example.attributes.f.schema[1].default).to.equal(10);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('f[c]: should be a string attribute', function () {
|
|
197
|
+
expect(data[0].example.attributes.f.schema[2].name).to.equal('c');
|
|
198
|
+
expect(data[0].example.attributes.f.schema[2].type).to.equal('string');
|
|
199
|
+
expect(data[0].example.attributes.f.schema[2].array).to.equal(false);
|
|
200
|
+
expect(data[0].example.attributes.f.schema[2].default).to.equal('hello');
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('f[d]: should be a vec3 attribute array', function () {
|
|
204
|
+
expect(data[0].example.attributes.f.schema[3].name).to.equal('d');
|
|
205
|
+
expect(data[0].example.attributes.f.schema[3].type).to.equal('vec3');
|
|
206
|
+
expect(data[0].example.attributes.f.schema[3].array).to.equal(true);
|
|
207
|
+
expect(data[0].example.attributes.f.schema[3].default).to.eql(null);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('g: should be a json attribute array with a size', function () {
|
|
211
|
+
expect(data[0].example.attributes.g).to.exist;
|
|
212
|
+
expect(data[0].example.attributes.g.name).to.equal('g');
|
|
213
|
+
expect(data[0].example.attributes.g.type).to.equal('json');
|
|
214
|
+
expect(data[0].example.attributes.g.array).to.equal(true);
|
|
215
|
+
expect(data[0].example.attributes.g.size).to.equal(2);
|
|
216
|
+
expect(data[0].example.attributes.g.default).to.eql(null);
|
|
217
|
+
|
|
218
|
+
expect(data[0].example.attributes.g.schema).to.exist;
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('h: should be a nested json attribute (parent)', function () {
|
|
222
|
+
expect(data[0].example.attributes.h).to.exist;
|
|
223
|
+
expect(data[0].example.attributes.h.name).to.equal('h');
|
|
224
|
+
expect(data[0].example.attributes.h.type).to.equal('json');
|
|
225
|
+
expect(data[0].example.attributes.h.array).to.equal(false);
|
|
226
|
+
expect(data[0].example.attributes.h.default).to.eql(null);
|
|
227
|
+
|
|
228
|
+
expect(data[0].example.attributes.h.schema).to.exist;
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('h[x]: should be a nested json attribute (child)', function () {
|
|
232
|
+
expect(data[0].example.attributes.h.schema[0].name).to.equal('x');
|
|
233
|
+
expect(data[0].example.attributes.h.schema[0].type).to.equal('json');
|
|
234
|
+
expect(data[0].example.attributes.h.schema[0].array).to.equal(false);
|
|
235
|
+
expect(data[0].example.attributes.h.schema[0].default).to.eql(null);
|
|
236
|
+
|
|
237
|
+
expect(data[0].example.attributes.h.schema[0].schema).to.exist;
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('i: should be an inline default json attribute', function () {
|
|
241
|
+
expect(data[0].example.attributes.i).to.exist;
|
|
242
|
+
expect(data[0].example.attributes.i.name).to.equal('i');
|
|
243
|
+
expect(data[0].example.attributes.i.type).to.equal('json');
|
|
244
|
+
expect(data[0].example.attributes.i.array).to.equal(false);
|
|
245
|
+
expect(data[0].example.attributes.i.default).to.eql(null);
|
|
246
|
+
|
|
247
|
+
expect(data[0].example.attributes.i.schema).to.exist;
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('l: should be an inline type json attribute', function () {
|
|
251
|
+
expect(data[0].example.attributes.l).to.exist;
|
|
252
|
+
expect(data[0].example.attributes.l.name).to.equal('l');
|
|
253
|
+
expect(data[0].example.attributes.l.type).to.equal('json');
|
|
254
|
+
expect(data[0].example.attributes.l.array).to.equal(false);
|
|
255
|
+
expect(data[0].example.attributes.l.default).to.eql(null);
|
|
256
|
+
|
|
257
|
+
expect(data[0].example.attributes.l.schema).to.exist;
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('m: should be a self reference json attribute', function () {
|
|
261
|
+
expect(data[0].example.attributes.m).to.exist;
|
|
262
|
+
expect(data[0].example.attributes.m.name).to.equal('m');
|
|
263
|
+
expect(data[0].example.attributes.m.type).to.equal('json');
|
|
264
|
+
expect(data[0].example.attributes.m.array).to.equal(false);
|
|
265
|
+
expect(data[0].example.attributes.m.default).to.eql(null);
|
|
266
|
+
|
|
267
|
+
expect(data[0].example.attributes.m.schema).to.exist;
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('n: should not be recognised as an attribute (no attribute tag)', function () {
|
|
271
|
+
expect(data[0].example.attributes.n).to.not.exist;
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('o: should not be recognised as an attribute (type not an interface)', function () {
|
|
275
|
+
expect(data[0].example.attributes.o).to.not.exist;
|
|
276
|
+
});
|
|
277
|
+
});
|