@playcanvas/attribute-parser 1.0.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.
Files changed (56) hide show
  1. package/.github/workflows/ci.yml +28 -0
  2. package/README.md +76 -0
  3. package/package.json +24 -0
  4. package/src/index.js +226 -0
  5. package/src/parsers/attribute-parser.js +280 -0
  6. package/src/parsers/parsing-error.js +14 -0
  7. package/src/parsers/script-parser.js +404 -0
  8. package/src/utils/attribute-utils.js +72 -0
  9. package/src/utils/generic-utils.js +18 -0
  10. package/src/utils/tag-utils.js +65 -0
  11. package/src/utils/ts-utils.js +501 -0
  12. package/test/fixtures/asset.invalid.js +26 -0
  13. package/test/fixtures/asset.valid.js +25 -0
  14. package/test/fixtures/checkbox.valid.js +23 -0
  15. package/test/fixtures/color.valid.js +23 -0
  16. package/test/fixtures/curve.invalid.js +28 -0
  17. package/test/fixtures/curve.valid.js +49 -0
  18. package/test/fixtures/entity.valid.js +23 -0
  19. package/test/fixtures/enum.valid.js +78 -0
  20. package/test/fixtures/export.import.js +3 -0
  21. package/test/fixtures/export.valid.js +23 -0
  22. package/test/fixtures/inherit.valid.js +52 -0
  23. package/test/fixtures/intellisense.valid.js +37 -0
  24. package/test/fixtures/json.invalid.js +22 -0
  25. package/test/fixtures/json.valid.js +103 -0
  26. package/test/fixtures/misc.invalid.js +25 -0
  27. package/test/fixtures/misc.valid.js +33 -0
  28. package/test/fixtures/numeric.invalid.js +38 -0
  29. package/test/fixtures/numeric.valid.js +63 -0
  30. package/test/fixtures/playcanvas.d.ts +47963 -0
  31. package/test/fixtures/program.invalid.js +11 -0
  32. package/test/fixtures/text.valid.js +30 -0
  33. package/test/fixtures/vector.invalid.js +13 -0
  34. package/test/fixtures/vector.valid.js +23 -0
  35. package/test/tests/invalid/asset.test.js +48 -0
  36. package/test/tests/invalid/curve.test.js +24 -0
  37. package/test/tests/invalid/json.test.js +28 -0
  38. package/test/tests/invalid/misc.test.js +42 -0
  39. package/test/tests/invalid/numeric.test.js +56 -0
  40. package/test/tests/invalid/program.test.js +18 -0
  41. package/test/tests/invalid/vector.test.js +29 -0
  42. package/test/tests/valid/asset.test.js +51 -0
  43. package/test/tests/valid/checkbox.test.js +50 -0
  44. package/test/tests/valid/color.test.js +50 -0
  45. package/test/tests/valid/curve.test.js +77 -0
  46. package/test/tests/valid/entity.test.js +50 -0
  47. package/test/tests/valid/enum.test.js +74 -0
  48. package/test/tests/valid/export.test.js +56 -0
  49. package/test/tests/valid/inherit.test.js +103 -0
  50. package/test/tests/valid/intellisense.test.js +36 -0
  51. package/test/tests/valid/json.test.js +150 -0
  52. package/test/tests/valid/misc.test.js +47 -0
  53. package/test/tests/valid/numeric.test.js +104 -0
  54. package/test/tests/valid/text.test.js +59 -0
  55. package/test/tests/valid/vector.test.js +54 -0
  56. package/test/utils.js +53 -0
@@ -0,0 +1,501 @@
1
+ import { DocNodeKind } from '@microsoft/tsdoc';
2
+ import { knownLibFilesForCompilerOptions } from '@typescript/vfs';
3
+ import ts from 'typescript';
4
+ import { ParsingError } from '../parsers/parsing-error.js';
5
+
6
+ export function createDefaultMapFromCDN(options, basePath, ts) {
7
+ var fsMap = new Map();
8
+ var files = knownLibFilesForCompilerOptions(options, ts);
9
+ var prefix = `${basePath}js/jsdoc-parser/types/`;
10
+
11
+ async function uncached() {
12
+ const contentPromises = files.map((lib) => {
13
+ return fetch(prefix + lib).then((resp) => {
14
+ if (!resp.ok) {
15
+ throw new Error(`Failed to fetch ${prefix + lib}: ${resp.statusText}`);
16
+ }
17
+ return resp.text();
18
+ });
19
+ });
20
+
21
+ const contents = await Promise.all(contentPromises);
22
+
23
+ contents.forEach(function (text, index) {
24
+ return fsMap.set('/' + files[index], text);
25
+ });
26
+
27
+ return fsMap;
28
+ }
29
+
30
+ return uncached();
31
+ }
32
+
33
+ /**
34
+ * Resolves the original symbol in case of alias.
35
+ * @param {import('typescript').TypeChecker} typeChecker - The TypeScript type checker
36
+ * @param {import('typescript').Symbol} symbol - The TypeScript symbol
37
+ * @returns {import('typescript').Symbol} - The resolved symbol
38
+ */
39
+ function resolveAliasedSymbol(typeChecker, symbol) {
40
+ while (symbol.flags & ts.SymbolFlags.Alias) {
41
+ symbol = typeChecker.getAliasedSymbol(symbol);
42
+ }
43
+ return symbol;
44
+ }
45
+
46
+ /**
47
+ * Returns an array of exported nodes from a TypeScript source file.
48
+ * @param {import('typescript').Program} program - The TypeScript program
49
+ * @param {import('typescript').SourceFile} sourceFile - The TypeScript source file
50
+ * @returns {Set<import('typescript').Node>} - A Set of exported nodes
51
+ */
52
+ export function getExportedNodes(program, sourceFile) {
53
+ if (!program || !sourceFile) {
54
+ return [];
55
+ }
56
+
57
+ const typeChecker = program.getTypeChecker();
58
+ const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
59
+
60
+ if (!moduleSymbol) {
61
+ return [];
62
+ }
63
+
64
+ const exportedSymbols = typeChecker.getExportsOfModule(moduleSymbol);
65
+
66
+ // Find the actual declaration nodes for each exported symbol
67
+ const exportedNodes = [];
68
+
69
+ exportedSymbols.forEach((symbol) => {
70
+ const resolvedSymbol = resolveAliasedSymbol(typeChecker, symbol);
71
+ if (resolvedSymbol.declarations) {
72
+ exportedNodes.push(...resolvedSymbol.declarations);
73
+ }
74
+ });
75
+
76
+ return new Set(exportedNodes);
77
+ }
78
+
79
+ /**
80
+ * Checks if the node or its aliased symbol is a class declaration.
81
+ *
82
+ * @param {import('typescript').Node} node - The TypeScript node
83
+ * @param {import('typescript').TypeChecker} typeChecker - The TypeScript type checker
84
+ * @returns {boolean} - True if the node or its original declaration is a class declaration, false otherwise
85
+ */
86
+ export function isAliasedClassDeclaration(node, typeChecker) {
87
+ if (ts.isClassDeclaration(node)) {
88
+ return true;
89
+ }
90
+ // Try to resolve the original declaration
91
+ if (ts.isIdentifier(node.name)) {
92
+ const symbol = typeChecker.getSymbolAtLocation(node.name);
93
+ if (symbol) {
94
+ const resolvedSymbol = resolveAliasedSymbol(typeChecker, symbol);
95
+ if (resolvedSymbol.declarations) {
96
+ return resolvedSymbol.declarations.some(ts.isClassDeclaration);
97
+ }
98
+ }
99
+ }
100
+ return false;
101
+ }
102
+
103
+ /**
104
+ * Determines if a TypeScript node inherits from a specific base class.
105
+ * @param {ts.ClassDeclaration} node - The TypeScript node to analyze
106
+ * @param {ts.TypeChecker} typeChecker - The TypeScript type checker
107
+ * @param {ts.Symbol} baseClassSymbol - The base class symbol to check inheritance against
108
+ * @returns {boolean} - True if the node inherits from the base class
109
+ */
110
+ export function inheritsFrom(node, typeChecker, baseClassSymbol) {
111
+ if (node.heritageClauses) {
112
+ for (const clause of node.heritageClauses) {
113
+ for (const type of clause.types) {
114
+ // Get the symbol of the expression
115
+ const symbol = typeChecker.getTypeAtLocation(type.expression).getSymbol();
116
+
117
+ // Check if the symbol is the base class symbol
118
+ if (symbol === baseClassSymbol) {
119
+ return true;
120
+ }
121
+
122
+ // Get the declaration of the symbol to check its heritage clauses
123
+ if (symbol?.declarations.length > 0) {
124
+ const declaration = symbol.declarations[0];
125
+ if (inheritsFrom(declaration, typeChecker, baseClassSymbol)) {
126
+ return true;
127
+ }
128
+ }
129
+ }
130
+ }
131
+ }
132
+ return false;
133
+ }
134
+
135
+ /**
136
+ * Recursively gathers and flat maps and nodes that meet a specific filter.
137
+ * @param {import('typescript').Node} node - The TypeScript node to analyze
138
+ * @param {function} filter - A function to filter the nodes
139
+ * @returns {import('typescript').Node[]} - An array of exported class declarations
140
+ */
141
+ export function flatMapAnyNodes(node, filter = () => true) {
142
+ const results = [];
143
+
144
+ function visit(node) {
145
+ // Check if this node is an exported class declaration
146
+ if (filter(node)) {
147
+ results.push(node);
148
+ }
149
+
150
+ // Visit each child node
151
+ ts.forEachChild(node, visit);
152
+ }
153
+
154
+ // Start the search with the root node
155
+ visit(node);
156
+
157
+ return results;
158
+ }
159
+
160
+ /**
161
+ * Takes a AST Node, and if it's a class declaration, it will return it's super classes
162
+ *
163
+ * @param {import('typescript').Node} node - The node to check
164
+ * @param {import('typescript').TypeChecker} typeChecker - The programs typechecker
165
+ * @returns {import('typescript').Node[]} - The analyzed node and any super classes in the inheritance chain
166
+ */
167
+ function getSuperClasses(node, typeChecker) {
168
+ const superClasses = [node];
169
+
170
+ let currentClass = node;
171
+
172
+ while (currentClass) {
173
+ const heritageClauses = currentClass.heritageClauses;
174
+ if (!heritageClauses) {
175
+ break;
176
+ }
177
+
178
+ const extendsClause = heritageClauses.find(clause => clause.token === ts.SyntaxKind.ExtendsKeyword);
179
+ if (!extendsClause || extendsClause.types.length === 0) {
180
+ break;
181
+ }
182
+
183
+ const superClassType = typeChecker.getTypeAtLocation(extendsClause.types[0]);
184
+ const superClassSymbol = superClassType.getSymbol();
185
+
186
+ if (!superClassSymbol) {
187
+ break;
188
+ }
189
+
190
+ const superClassDeclaration = superClassSymbol.declarations.find(ts.isClassDeclaration);
191
+ if (!superClassDeclaration) {
192
+ break;
193
+ }
194
+
195
+ superClasses.push(superClassDeclaration);
196
+ currentClass = superClassDeclaration;
197
+ }
198
+
199
+ return superClasses; // .reverse(); // To return the array in order from the base class to the top-level class
200
+ }
201
+
202
+ /**
203
+ * Retrieves the JSDoc-style comments associated with a specific AST node.
204
+ *
205
+ * @param {import('typescript').Node} node - The TypeScript node to analyze
206
+ * @param {string} text - The full text content of the source file
207
+ * @param {import('typescript').TypeChecker} typeChecker - The TypeScript type checker
208
+ * @returns {{ memberName: string, member: ts.Node, range: import('typescript').CommentRange}[]} - An array of comment ranges
209
+ */
210
+ export function getJSDocCommentRanges(node, text, typeChecker) {
211
+ const commentRanges = [];
212
+
213
+ if (ts.isClassDeclaration(node)) {
214
+ // get an array of the class an all parent classed
215
+ const heritageChain = getSuperClasses(node, typeChecker);
216
+
217
+ // iterate over the heritance chain
218
+ heritageChain.forEach((classNode) => {
219
+ // for each class iterate over it's class members
220
+ classNode.members.forEach((member) => {
221
+ if (ts.isPropertyDeclaration(member) || ts.isSetAccessor(member)) {
222
+ const memberName = member.name && ts.isIdentifier(member.name) ? member.name.text : 'unnamed';
223
+ const ranges = getLeadingBlockCommentRanges(member, text);
224
+ if (ranges.length > 0) {
225
+ commentRanges.push({
226
+ memberName,
227
+ member,
228
+ range: ranges[0]
229
+ });
230
+ }
231
+ }
232
+ });
233
+ });
234
+ }
235
+
236
+ return commentRanges;
237
+ }
238
+
239
+ const ASTERISK = 42;
240
+ const SLASH = 47;
241
+
242
+ /**
243
+ * Retrieves the JSDoc-style comments blocks associated with a specific AST node.
244
+ * @param {import('typescript').Node} node - The TypeScript node to analyze
245
+ * @param {string} text - The full text content of the source file
246
+ * @returns {import('typescript').CommentRange[]} - An array of comment ranges
247
+ */
248
+ export function getLeadingBlockCommentRanges(node, text) {
249
+ const ranges = ts.getLeadingCommentRanges(text, node.getFullStart()) ?? [];
250
+ return ranges.filter(({ pos }) => {
251
+ return (
252
+ text.charCodeAt(pos + 1) === ASTERISK &&
253
+ text.charCodeAt(pos + 2) === ASTERISK &&
254
+ text.charCodeAt(pos + 3) !== SLASH
255
+ );
256
+ });
257
+ }
258
+
259
+ /**
260
+ * Recursively extract text content from a DocNode.
261
+ * @param {tsdoc.DocNode} node - The DocNode from which to extract text.
262
+ * @returns {string} - The concatenated string of all text content.
263
+ */
264
+ export function extractTextFromDocNode(node) {
265
+ // Check if the current node is a PlainText node
266
+ if (node.kind === DocNodeKind.PlainText) {
267
+ return node.text.trim(); // Cast and return as we found a PlainText node
268
+ }
269
+
270
+ // If not, recursively search its children
271
+ for (const child of node.getChildNodes()) {
272
+ const result = extractTextFromDocNode(child);
273
+ if (result !== null) {
274
+ return result; // Return the first PlainText node found in the subtree
275
+ }
276
+ }
277
+
278
+ return null;
279
+ }
280
+
281
+ /**
282
+ * Extracts all imports from a TypeScript source file.
283
+ * @param {ts.Node} node - The TypeScript node to analyze
284
+ * @returns {string[]} - An array of import statements
285
+ */
286
+ export function getLeadingComments(node) {
287
+ const fullText = node.getFullText();
288
+ const commentRanges = ts.getLeadingCommentRanges(fullText, 0);
289
+
290
+ if (!commentRanges) return [];
291
+ return commentRanges.map(range => fullText.slice(range.pos, range.end));
292
+ }
293
+
294
+ /**
295
+ * Determines if a TypeScript node is an enum.
296
+ * @param {import('typescript').Node} node - The TypeScript node to analyze
297
+ * @returns {boolean} - True if the node is an enum
298
+ */
299
+ export function isEnum(node) {
300
+ // Check for TypeScript native enum
301
+ if (ts.isEnumDeclaration(node)) {
302
+ return true; // The node is a TypeScript enum
303
+ }
304
+
305
+ // Check for JSDoc @enum tag in a variable declaration
306
+ if (ts.isVariableDeclaration(node)) {
307
+ const jsDocs = ts.getJSDocTags(node);
308
+ const hasEnumTag = jsDocs.some(doc => doc.tagName.text === 'enum');
309
+ return hasEnumTag;
310
+ }
311
+
312
+ return false;
313
+ }
314
+
315
+ /**
316
+ * Gets the inferred type of a TypeScript node.
317
+ *
318
+ * @param {ts.Node} node - The TypeScript node to analyze
319
+ * @param {ts.TypeChecker} typeChecker - The TypeScript type checker
320
+ * @returns {{ array: boolean, name: string | undefined, type: ts.Type | null }} - The inferred type of the node
321
+ */
322
+ export function getType(node, typeChecker) {
323
+ if (node?.name?.kind === ts.SyntaxKind.Identifier) {
324
+ const type = typeChecker.getTypeAtLocation(node);
325
+ const array = typeChecker.isArrayType(type);
326
+ const actualType = array ? typeChecker.getElementTypeOfArrayType(type) : type;
327
+ const name = typeChecker.typeToString(actualType);
328
+
329
+ return { type: actualType, name, array };
330
+ }
331
+
332
+ return null;
333
+ }
334
+
335
+ /**
336
+ * Parses a numerical string into a number. Handles hexadecimal, binary,
337
+ * octal, exponential notation and underscore notation
338
+ *
339
+ * @param {string} input - The string to parse
340
+ * @returns {number} The parsed number, or NaN if the input is not a number
341
+ */
342
+ export function parseNumber(input) {
343
+ // Remove underscores from the string to handle underscore notation
344
+ let cleanedInput = input.replace(/_/g, '').toLowerCase();
345
+
346
+ // Identify and isolate unary operators
347
+ let unaryOperator = null;
348
+ if (cleanedInput.startsWith('+') || cleanedInput.startsWith('-') || cleanedInput.startsWith('~')) {
349
+ unaryOperator = cleanedInput[0];
350
+ cleanedInput = cleanedInput.slice(1).trim();
351
+ }
352
+
353
+ // Check for different bases and handle appropriately
354
+ let result;
355
+ if (cleanedInput === 'NaN' || cleanedInput === '') {
356
+ result = NaN;
357
+ } else if (cleanedInput.startsWith('0b')) {
358
+ result = parseInt(cleanedInput.slice(2), 2);
359
+ } else if (cleanedInput.startsWith('0o')) {
360
+ result = parseInt(cleanedInput.slice(2), 8);
361
+ } else if (cleanedInput.startsWith('0x')) {
362
+ result = parseInt(cleanedInput.slice(2), 16);
363
+ } else if (cleanedInput.includes('e')) {
364
+ result = parseFloat(cleanedInput); // Exponential notation
365
+ } else if (!isNaN(Number(cleanedInput))) {
366
+ result = Number(cleanedInput); // Decimal, including floating-point
367
+ } else {
368
+ result = NaN;
369
+ }
370
+
371
+ // Apply unary operator if present
372
+ if (unaryOperator === '+') {
373
+ result = +result; // Positive sign can be ignored, but explicitly using for clarity
374
+ } else if (unaryOperator === '-') {
375
+ result = -result;
376
+ } else if (unaryOperator === '~') {
377
+ result = ~result; // Bitwise NOT
378
+ }
379
+
380
+ return result;
381
+ }
382
+
383
+ /**
384
+ * Parses a string representationn of a numerical array into an array of numbers.
385
+ *
386
+ * @param {string} input - The string to parse
387
+ * @returns {number[]} - The parsed array of numbers
388
+ * @throws {Error} - If the input contains invalid characters or is not a valid array
389
+ */
390
+ export function parseStringToNumericalArray(input) {
391
+ // Remove unnecessary whitespace
392
+ const sanitizedInput = input.replace(/\s+/g, '');
393
+
394
+ // Check if the input still contains valid array format
395
+ if (!sanitizedInput.startsWith('[') || !sanitizedInput.endsWith(']')) {
396
+ throw new Error('Invalid input string');
397
+ }
398
+
399
+ // Remove the brackets and split the string into an array
400
+ const arrayString = sanitizedInput.slice(1, -1);
401
+ const elements = arrayString.split(',');
402
+
403
+ // Parse each element using the parseNumber function
404
+ const parsedArray = elements.map((element) => {
405
+ return parseNumber(element.trim());
406
+ });
407
+
408
+ // Validate if the result is an array and contains only valid numbers
409
+ if (parsedArray.some(isNaN)) {
410
+ throw new Error('Parsed result contains invalid numbers');
411
+ }
412
+
413
+ return parsedArray;
414
+ }
415
+
416
+ /**
417
+ * Checks if the given node is a numerical literal or a unary expression.
418
+ * @param {import('typescript').Node} node - The node to check
419
+ * @returns {boolean} - True if the node is a numeric literal
420
+ */
421
+ export const isNumericLiteral = (node) => {
422
+ // Is the node a numeric literal '10'
423
+ const isNumericLiteral = ts.isNumericLiteral(node);
424
+
425
+ // Or is it a +/- unary expression '-10' or '+10'
426
+ const isUnaryExpression =
427
+ ts.isUnaryExpression(node) && //
428
+ (node.operator === ts.SyntaxKind.MinusToken || node.operator === ts.SyntaxKind.PlusToken) &&
429
+ node.operand.kind === ts.SyntaxKind.NumericLiteral;
430
+
431
+ return isNumericLiteral || isUnaryExpression;
432
+ };
433
+
434
+ /**
435
+ * If the given node is a numeric literal, returns the parsed number.
436
+ * @param {import('typescript').Node} node - The node to check
437
+ * @param {boolean} [isArgument] - True if the node is an argument in a parameter list
438
+ * @returns {number} - The parsed number
439
+ * @throws {ParsingError} - If the node is not a numeric literal
440
+ */
441
+ export const parseFloatNode = (node, isArgument = false) => {
442
+ if (!isNumericLiteral(node)) {
443
+ if (ts.isPropertyAssignment(node.parent) || ts.isPropertyDeclaration(node.parent) || isArgument) {
444
+ throw new ParsingError(node, `"${node.parent.getText()}" must be defined with a numeric literal.`);
445
+ }
446
+ return 0;
447
+ }
448
+ return parseNumber(node.getText());
449
+ };
450
+
451
+ /**
452
+ * If the given node is a boolean literal, returns the parsed boolean.
453
+ * @param {import('typescript').Node} node - The node to check
454
+ * @returns {boolean} - The parsed boolean
455
+ */
456
+ export const parseBooleanNode = (node) => {
457
+ if (node.kind !== ts.SyntaxKind.TrueKeyword && node.kind !== ts.SyntaxKind.FalseKeyword) {
458
+ if (ts.isPropertyAssignment(node.parent) || ts.isPropertyDeclaration(node.parent)) {
459
+ throw new ParsingError(node, `"${node.parent.getText()}" must be defined with a boolean literal.`);
460
+ }
461
+ return false;
462
+ }
463
+ return node.kind === ts.SyntaxKind.TrueKeyword;
464
+ };
465
+
466
+ /**
467
+ * If the given node is a string literal, returns the parsed string.
468
+ * @param {import('typescript').Node} node - The node to check
469
+ * @returns {string} - The parsed string
470
+ */
471
+ export const parseStringNode = (node) => {
472
+ if (node.kind !== ts.SyntaxKind.StringLiteral) {
473
+ if (ts.isPropertyAssignment(node.parent) || ts.isPropertyDeclaration(node.parent)) {
474
+ throw new ParsingError(node, `"${node.parent.getText()}" must be defined with a string literal.`);
475
+ }
476
+ return '';
477
+ }
478
+ return node.getText().slice(1, -1);
479
+ };
480
+
481
+ export const parseArrayLiteralNode = (node, isArgument = false) => {
482
+ if (node.kind !== ts.SyntaxKind.ArrayLiteralExpression) {
483
+ if (ts.isPropertyAssignment(node.parent) || ts.isPropertyDeclaration(node.parent) || isArgument) {
484
+ throw new ParsingError(node, `"${node.parent.getText()}" must be defined with an array literal.`);
485
+ }
486
+ return [];
487
+ }
488
+ return JSON.parse(node.getText());
489
+ };
490
+
491
+ /**
492
+ * Parses the first argument as an array literal.
493
+ * @param {ts.Node[]} args - The arguments to parse
494
+ * @returns {number[]} - The parsed array
495
+ */
496
+ export const parseArrayLiteral = (args) => {
497
+ if (args.length > 0) {
498
+ return parseArrayLiteralNode(args[0], true);
499
+ }
500
+ return [];
501
+ };
@@ -0,0 +1,26 @@
1
+ import { Script, Asset } from 'playcanvas';
2
+
3
+ class Example extends Script {
4
+ /**
5
+ * @attribute
6
+ * @type {Asset}
7
+ * @resource nothing
8
+ */
9
+ a;
10
+
11
+ /**
12
+ * @attribute
13
+ * @type {Asset}
14
+ * @resource 1
15
+ */
16
+ b;
17
+
18
+ /**
19
+ * @attribute
20
+ * @type {Asset}
21
+ * @resource
22
+ */
23
+ c;
24
+ }
25
+
26
+ export { Example };
@@ -0,0 +1,25 @@
1
+ import { Script, Asset } from 'playcanvas';
2
+
3
+ class Example extends Script {
4
+ /**
5
+ * @attribute
6
+ * @type {Asset}
7
+ */
8
+ a;
9
+
10
+ /**
11
+ * @attribute
12
+ * @type {Asset}
13
+ * @resource texture
14
+ */
15
+ b;
16
+
17
+ /**
18
+ * @attribute
19
+ * @type {Asset[]}
20
+ * @resource container
21
+ */
22
+ c;
23
+ }
24
+
25
+ export { Example };
@@ -0,0 +1,23 @@
1
+ import { Script } from 'playcanvas';
2
+
3
+ class Example extends Script {
4
+ /**
5
+ * @attribute
6
+ * @type {boolean}
7
+ */
8
+ a;
9
+
10
+ /**
11
+ * @attribute
12
+ */
13
+ b = true;
14
+
15
+ /**
16
+ * @attribute
17
+ * @type {boolean[]}
18
+ * @size 2
19
+ */
20
+ c;
21
+ }
22
+
23
+ export { Example };
@@ -0,0 +1,23 @@
1
+ import { Script, Color } from 'playcanvas';
2
+
3
+ class Example extends Script {
4
+ /**
5
+ * @attribute
6
+ * @type {Color}
7
+ */
8
+ a;
9
+
10
+ /**
11
+ * @attribute
12
+ */
13
+ b = new Color(1, 0, 0, 1);
14
+
15
+ /**
16
+ * @attribute
17
+ * @type {Color[]}
18
+ * @size 2
19
+ */
20
+ c;
21
+ }
22
+
23
+ export { Example };
@@ -0,0 +1,28 @@
1
+ import { Script, Curve } from 'playcanvas';
2
+
3
+ class Example extends Script {
4
+ /**
5
+ * @attribute
6
+ * @type {Curve}
7
+ * @color bad
8
+ */
9
+ a;
10
+
11
+ /**
12
+ * @attribute
13
+ * @type {Curve}
14
+ * @color rgb
15
+ * @curves ['x', 'y', 'z', 'w']
16
+ */
17
+ b;
18
+
19
+ /**
20
+ * @attribute
21
+ * @type {Curve}
22
+ * @color 1
23
+ * @curves 1
24
+ */
25
+ c;
26
+ }
27
+
28
+ export { Example };
@@ -0,0 +1,49 @@
1
+ import { Script, Curve } from 'playcanvas';
2
+
3
+ class Example extends Script {
4
+ /**
5
+ * @attribute
6
+ * @type {Curve}
7
+ */
8
+ a;
9
+
10
+ /**
11
+ * @attribute
12
+ */
13
+ b = new Curve([
14
+ 0, 0,
15
+ 1, 1
16
+ ]);
17
+
18
+ /**
19
+ * @attribute
20
+ * @type {Curve}
21
+ * @color r
22
+ */
23
+ c;
24
+
25
+ /**
26
+ * @attribute
27
+ * @type {Curve}
28
+ * @color rgb
29
+ * @curves ['x', 'y', 'z']
30
+ */
31
+ d;
32
+
33
+ /**
34
+ * @attribute
35
+ * @type {Curve[]}
36
+ * @size 2
37
+ */
38
+ e;
39
+
40
+ /**
41
+ * @attribute
42
+ * @type {Curve}
43
+ * @color rgba
44
+ * @curves ["x", 'y', `z`]
45
+ */
46
+ f;
47
+ }
48
+
49
+ export { Example };
@@ -0,0 +1,23 @@
1
+ import { Script, Entity } from 'playcanvas';
2
+
3
+ class Example extends Script {
4
+ /**
5
+ * @attribute
6
+ * @type {Entity}
7
+ */
8
+ a;
9
+
10
+ /**
11
+ * @attribute
12
+ */
13
+ b = new Entity();
14
+
15
+ /**
16
+ * @attribute
17
+ * @type {Entity[]}
18
+ * @size 2
19
+ */
20
+ c;
21
+ }
22
+
23
+ export { Example };