@playcanvas/attribute-parser 1.0.8 → 1.2.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/parsers/attribute-parser.js +12 -21
- package/src/parsers/script-parser.js +15 -7
- package/src/utils/tag-utils.js +24 -16
- package/src/utils/ts-utils.js +164 -3
- package/test/fixtures/asset.invalid.ts +24 -0
- package/test/fixtures/asset.valid.ts +21 -0
- package/test/fixtures/enum.valid.js +6 -22
- package/test/fixtures/enum.valid.ts +49 -0
- package/test/fixtures/json.valid.ts +72 -0
- package/test/fixtures/program.valid.js +6 -2
- 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/test/tests/valid/program.test.js +1 -0
package/package.json
CHANGED
|
@@ -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,10 +167,10 @@ 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
|
-
value = serializer(node.initializer ?? node);
|
|
173
|
+
value = serializer(node.initializer ?? node, this.typeChecker);
|
|
174
174
|
} catch (error) {
|
|
175
175
|
errors.push(error);
|
|
176
176
|
return;
|
|
@@ -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
|
});
|
|
@@ -5,7 +5,7 @@ import { AttributeParser } from './attribute-parser.js';
|
|
|
5
5
|
import { ParsingError } from './parsing-error.js';
|
|
6
6
|
import { hasTag } from '../utils/attribute-utils.js';
|
|
7
7
|
import { zipArrays } from '../utils/generic-utils.js';
|
|
8
|
-
import { flatMapAnyNodes, getJSDocCommentRanges,
|
|
8
|
+
import { flatMapAnyNodes, getJSDocCommentRanges, getLiteralValue, parseArrayLiteral, parseFloatNode } from '../utils/ts-utils.js';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* @typedef {object} Attribute
|
|
@@ -77,9 +77,9 @@ const SUPPORTED_INITIALIZABLE_TYPE_NAMES = new Map([
|
|
|
77
77
|
['Vec3', createNumberArgumentParser('Vec3', [0, 0, 0])],
|
|
78
78
|
['Vec4', createNumberArgumentParser('Vec4', [0, 0, 0, 0])],
|
|
79
79
|
['Color', createNumberArgumentParser('Color', [1, 1, 1, 1])],
|
|
80
|
-
['number',
|
|
81
|
-
['string',
|
|
82
|
-
['boolean',
|
|
80
|
+
['number', getLiteralValue],
|
|
81
|
+
['string', getLiteralValue],
|
|
82
|
+
['boolean', getLiteralValue]
|
|
83
83
|
]);
|
|
84
84
|
|
|
85
85
|
/**
|
|
@@ -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
|
/**
|
|
@@ -187,8 +188,15 @@ const mapAttributesToOutput = (attribute) => {
|
|
|
187
188
|
// remove enum if it's empty
|
|
188
189
|
if (attribute.enum.length === 0) delete attribute.enum;
|
|
189
190
|
|
|
191
|
+
// If the attribute has no default value then set it
|
|
192
|
+
if (attribute.value === undefined) {
|
|
193
|
+
if (attribute.type === 'string') attribute.value = '';
|
|
194
|
+
if (attribute.type === 'number') attribute.value = 0;
|
|
195
|
+
if (attribute.type === 'boolean') attribute.value = false;
|
|
196
|
+
}
|
|
197
|
+
|
|
190
198
|
// set the default value
|
|
191
|
-
if (attribute.value !== undefined) attribute.default = attribute.value;
|
|
199
|
+
if (attribute.value !== undefined) attribute.default = attribute.default ?? attribute.value;
|
|
192
200
|
|
|
193
201
|
// Curve Attributes specifically should not expose a default value if it's an empty array
|
|
194
202
|
if (attribute.type === 'curve' && Array.isArray(attribute.value) && attribute.value.length === 0) {
|
|
@@ -244,7 +252,7 @@ export class ScriptParser {
|
|
|
244
252
|
const typeName = this.typeChecker.typeToString(type);
|
|
245
253
|
const serializer = SUPPORTED_INITIALIZABLE_TYPE_NAMES.get(typeName);
|
|
246
254
|
if (serializer) {
|
|
247
|
-
this.typeSerializerMap.set(
|
|
255
|
+
this.typeSerializerMap.set(typeName, serializer);
|
|
248
256
|
}
|
|
249
257
|
});
|
|
250
258
|
|
package/src/utils/tag-utils.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { DiagnosticCategory } from 'typescript';
|
|
2
|
+
|
|
1
3
|
import { parseNumber, parseStringToNumericalArray } from './ts-utils.js';
|
|
2
4
|
|
|
3
5
|
/**
|
|
@@ -35,31 +37,37 @@ export function parseTag(input = '') {
|
|
|
35
37
|
|
|
36
38
|
/**
|
|
37
39
|
* Validates that a tag value matches the expected type
|
|
38
|
-
* @throws {Error} - If the tag value is not the expected type
|
|
39
40
|
*
|
|
40
|
-
* @param {String} value - The
|
|
41
|
-
* @param {
|
|
42
|
-
* @param {import('@typescript/vfs').VirtualTypeScriptEnvironment} env - The environment
|
|
43
|
-
* @
|
|
41
|
+
* @param {String} value - The value to be validated.
|
|
42
|
+
* @param {String} typeAnnotation - The TypeScript type annotation as a string.
|
|
43
|
+
* @param {import('@typescript/vfs').VirtualTypeScriptEnvironment} env - The environment containing the language service and file creation utilities.
|
|
44
|
+
* @throws Will throw an error if the value does not conform to the typeAnnotation.
|
|
45
|
+
* @returns {true} if validation passes without type errors.
|
|
44
46
|
*/
|
|
45
47
|
export function validateTag(value, typeAnnotation, env) {
|
|
46
|
-
|
|
48
|
+
const virtualFileName = '/___virtual__.ts';
|
|
47
49
|
const sourceText = `let a: ${typeAnnotation} = ${value};`;
|
|
48
|
-
env.createFile('/___virtual__.ts', sourceText);
|
|
49
50
|
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
// Create or overwrite the virtual file with the new source text
|
|
52
|
+
env.createFile(virtualFileName, sourceText);
|
|
53
|
+
|
|
54
|
+
// Retrieve the language service from the environment
|
|
55
|
+
const languageService = env.languageService;
|
|
53
56
|
|
|
54
|
-
//
|
|
55
|
-
const
|
|
57
|
+
// Fetch semantic diagnostics only for the virtual file
|
|
58
|
+
const errors = languageService.getSemanticDiagnostics(virtualFileName);
|
|
56
59
|
|
|
57
|
-
//
|
|
58
|
-
const
|
|
60
|
+
// Filter for type assignment errors (Error Code 2322: Type 'X' is not assignable to type 'Y')
|
|
61
|
+
const typeErrors = errors.filter(
|
|
62
|
+
error => error.code === 2322 && error.category === DiagnosticCategory.Error
|
|
63
|
+
);
|
|
59
64
|
|
|
60
|
-
|
|
61
|
-
|
|
65
|
+
// If any type error is found, throw an error with the diagnostic message
|
|
66
|
+
if (typeErrors.length > 0) {
|
|
67
|
+
const errorMessage = typeErrors[0].messageText.toString();
|
|
68
|
+
throw new Error(`Type Validation Error: ${errorMessage}`);
|
|
62
69
|
}
|
|
63
70
|
|
|
71
|
+
// If no type errors are found, return true indicating successful validation
|
|
64
72
|
return true;
|
|
65
73
|
}
|
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
|
}
|
|
@@ -461,6 +486,142 @@ export const parseBooleanNode = (node) => {
|
|
|
461
486
|
return node.kind === ts.SyntaxKind.TrueKeyword;
|
|
462
487
|
};
|
|
463
488
|
|
|
489
|
+
function resolveIdentifier(node, typeChecker) {
|
|
490
|
+
const symbol = typeChecker.getSymbolAtLocation(node);
|
|
491
|
+
if (symbol && symbol.declarations) {
|
|
492
|
+
for (const declaration of symbol.declarations) {
|
|
493
|
+
if (ts.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
494
|
+
return getLiteralValue(declaration.initializer, typeChecker);
|
|
495
|
+
}
|
|
496
|
+
// Handle other kinds of declarations if needed
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
return undefined;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Resolve the value of a property access expression. Limited to simple cases like
|
|
504
|
+
* object literals and variable declarations.
|
|
505
|
+
*
|
|
506
|
+
* @param {import('typescript').Node} node - The property access expression node
|
|
507
|
+
* @param {import('typescript')} typeChecker - The TypeScript type checker
|
|
508
|
+
* @returns {any} - The resolved value of the property access
|
|
509
|
+
*/
|
|
510
|
+
const resolvePropertyAccess = (node, typeChecker) => {
|
|
511
|
+
const symbol = typeChecker.getSymbolAtLocation(node);
|
|
512
|
+
if (symbol && symbol.declarations) {
|
|
513
|
+
for (const declaration of symbol.declarations) {
|
|
514
|
+
if (ts.isPropertyAssignment(declaration) && declaration.initializer) {
|
|
515
|
+
return getLiteralValue(declaration.initializer, typeChecker);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (ts.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
519
|
+
return getLiteralValue(declaration.initializer, typeChecker);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (ts.isEnumMember(declaration)) {
|
|
523
|
+
return declaration.initializer ? getLiteralValue(declaration.initializer, typeChecker) : declaration.name.getText();
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// If symbol not found directly, attempt to resolve the object first
|
|
530
|
+
const objValue = getLiteralValue(node.expression, typeChecker);
|
|
531
|
+
if (objValue && typeof objValue === 'object') {
|
|
532
|
+
const propName = node.name.text;
|
|
533
|
+
return objValue[propName];
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
return undefined;
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Evaluates unary prefixes like +, -, !, ~, and returns the result.
|
|
541
|
+
* @param {import('typescript').Node} node - The AST node to evaluate
|
|
542
|
+
* @param {import('typescript').TypeChecker} typeChecker - The TypeScript type checker
|
|
543
|
+
* @returns {number | boolean | undefined} - The result of the evaluation
|
|
544
|
+
*/
|
|
545
|
+
const evaluatePrefixUnaryExpression = (node, typeChecker) => {
|
|
546
|
+
const operandValue = getLiteralValue(node.operand, typeChecker);
|
|
547
|
+
if (operandValue !== undefined) {
|
|
548
|
+
switch (node.operator) {
|
|
549
|
+
case ts.SyntaxKind.PlusToken:
|
|
550
|
+
return +operandValue;
|
|
551
|
+
case ts.SyntaxKind.MinusToken:
|
|
552
|
+
return -operandValue;
|
|
553
|
+
case ts.SyntaxKind.ExclamationToken:
|
|
554
|
+
return !operandValue;
|
|
555
|
+
case ts.SyntaxKind.TildeToken:
|
|
556
|
+
return ~operandValue;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return undefined;
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
function handleObjectLiteral(node, typeChecker) {
|
|
563
|
+
const obj = {};
|
|
564
|
+
node.properties.forEach((prop) => {
|
|
565
|
+
if (ts.isPropertyAssignment(prop)) {
|
|
566
|
+
const key = prop.name.getText();
|
|
567
|
+
const value = getLiteralValue(prop.initializer, typeChecker);
|
|
568
|
+
obj[key] = value;
|
|
569
|
+
} else if (ts.isShorthandPropertyAssignment(prop)) {
|
|
570
|
+
const key = prop.name.getText();
|
|
571
|
+
const value = resolveIdentifier(prop.name, typeChecker);
|
|
572
|
+
obj[key] = value;
|
|
573
|
+
}
|
|
574
|
+
});
|
|
575
|
+
return obj;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Attempts to extract a literal value from a TypeScript node. This function
|
|
580
|
+
* supports various types of literals and expressions, including object literals,
|
|
581
|
+
* array literals, identifiers, and unary expressions.
|
|
582
|
+
*
|
|
583
|
+
* @param {import('typescript').Node} node - The AST node to evaluate
|
|
584
|
+
* @param {import('typescript').TypeChecker} typeChecker - The TypeScript type checker
|
|
585
|
+
* @returns {any} - The extracted literal value
|
|
586
|
+
*/
|
|
587
|
+
export function getLiteralValue(node, typeChecker) {
|
|
588
|
+
if (!node) return undefined;
|
|
589
|
+
|
|
590
|
+
if (ts.isLiteralExpression(node) || ts.isBooleanLiteral(node)) {
|
|
591
|
+
if (ts.isStringLiteral(node)) {
|
|
592
|
+
return node.text;
|
|
593
|
+
}
|
|
594
|
+
if (ts.isNumericLiteral(node)) {
|
|
595
|
+
return Number(node.text);
|
|
596
|
+
}
|
|
597
|
+
if (node.kind === ts.SyntaxKind.TrueKeyword) {
|
|
598
|
+
return true;
|
|
599
|
+
}
|
|
600
|
+
if (node.kind === ts.SyntaxKind.FalseKeyword) {
|
|
601
|
+
return false;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
switch (node.kind) {
|
|
606
|
+
case ts.SyntaxKind.NullKeyword:
|
|
607
|
+
return null;
|
|
608
|
+
case ts.SyntaxKind.ArrayLiteralExpression:
|
|
609
|
+
return (node).elements.map(element => getLiteralValue(element, typeChecker));
|
|
610
|
+
case ts.SyntaxKind.ObjectLiteralExpression:
|
|
611
|
+
return handleObjectLiteral(node, typeChecker);
|
|
612
|
+
case ts.SyntaxKind.Identifier:
|
|
613
|
+
return resolveIdentifier(node, typeChecker);
|
|
614
|
+
case ts.SyntaxKind.PropertyAccessExpression:
|
|
615
|
+
return resolvePropertyAccess(node, typeChecker);
|
|
616
|
+
case ts.SyntaxKind.ParenthesizedExpression:
|
|
617
|
+
return getLiteralValue((node).expression, typeChecker);
|
|
618
|
+
case ts.SyntaxKind.PrefixUnaryExpression:
|
|
619
|
+
return evaluatePrefixUnaryExpression(node, typeChecker);
|
|
620
|
+
default:
|
|
621
|
+
return undefined;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
464
625
|
/**
|
|
465
626
|
* If the given node is a string literal, returns the parsed string.
|
|
466
627
|
* @param {import('typescript').Node} node - The node to check
|
|
@@ -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
|
*/
|
|
@@ -44,7 +34,7 @@ class Example extends Script {
|
|
|
44
34
|
* @attribute
|
|
45
35
|
* @type {NumberEnum}
|
|
46
36
|
*/
|
|
47
|
-
e;
|
|
37
|
+
e = NumberEnum.A;
|
|
48
38
|
|
|
49
39
|
/**
|
|
50
40
|
* @attribute
|
|
@@ -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 };
|
|
@@ -8,13 +8,17 @@ export const MyEnum = { value: 0 };
|
|
|
8
8
|
class Example extends Script {
|
|
9
9
|
/**
|
|
10
10
|
* @attribute
|
|
11
|
-
* @
|
|
11
|
+
* @precision 1
|
|
12
|
+
* @type {number}
|
|
12
13
|
*/
|
|
13
|
-
a
|
|
14
|
+
a;
|
|
14
15
|
|
|
15
16
|
initialize() {
|
|
16
17
|
confetti();
|
|
17
18
|
new TWEEN.Tween({ x: 0 }).to({ x: 100 }, 1000).start();
|
|
19
|
+
|
|
20
|
+
// This is an intentional type error, but the parser should ignore these
|
|
21
|
+
this.a = 'string';
|
|
18
22
|
}
|
|
19
23
|
}
|
|
20
24
|
|
|
@@ -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
|
+
});
|