playcanvas / playcanvas/attribute-parser
Parser throws on tuple `@type` tags and `= {}` initializers, and silently drops unresolvable types
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 1
- Forks
- 2
- PR merge metrics
- No merged PRs in 30d
Description
Found while investigating a forum report about the Interface Attribute Arrays docs example (docs fix: playcanvas/developer-site#1200). Interface arrays themselves work correctly — these are three separate robustness problems I hit while narrowing it down.
All three reproduce on main (fa36853, v1.11.0) through parseAttributes(), with the playcanvas types loaded as in test/utils.ts. Each snippet below is a complete script file.
1. TypeError when @type is a tuple
import { Script } from 'playcanvas';
class GameLogic extends Script {
static scriptName = 'gameLogic';
/**
* @attribute
* @type {[number]}
*/
values;
}
export { GameLogic };
TypeError: Cannot read properties of undefined (reading 'declarations')
at ScriptParser.extractAttributes
at JSDocParser.parseAttributes
src/parsers/script-parser.ts:497-498:
const symbol = type.aliasSymbol || type.symbol;
const typeNode = (symbol?.valueDeclaration || symbol.declarations?.[0]) as InternalNode;
A tuple type has no symbol, so symbol?.valueDeclaration is undefined and the second term dereferences undefined. symbol?.declarations?.[0] would fall through to the if (!typeNode) continue guard on the next line. Same for @type {[number, number]}.
2. TypeError when an attribute is initialized with an empty object literal
import { Script } from 'playcanvas';
class GameLogic extends Script {
static scriptName = 'gameLogic';
/**
* @attribute
*/
data = {};
}
export { GameLogic };
TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator))
at Array.from (<anonymous>)
at ScriptParser.extractAttributes
at JSDocParser.parseAttributes
src/parsers/script-parser.ts:553-560:
const members: readonly ts.Node[] =
typeNode.members ??
typeNode.properties ??
Array.from(type.symbol.members as unknown as Iterable<[string, ts.Symbol]>).map(
(entry) => entry[1].declarations[0]
) ??
[];
For = {} both typeNode.members and typeNode.properties are nullish and type.symbol.members is undefined, so Array.from throws. Note the trailing ?? [] can never catch this, since the throw happens while evaluating the operand. data = { x: 1 } is fine — only the empty literal hits it.
This also fires for a member of an /** @interface */ class, e.g. data = {}; inside class Enemy.
Why 1 and 2 matter
Both throw out of extractAttributes, so the consumer gets no attributes and no diagnostics for the whole file instead of one error on the offending member. A half-written type annotation is a normal intermediate state while typing in the Editor's code editor, so these are easy to hit. Reporting a ParsingError for the member (as the neighbouring paths do) would keep the rest of the script parsing.
3. An unresolvable @type is dropped silently
import { Script } from 'playcanvas';
class GameLogic extends Script {
static scriptName = 'gameLogic';
/**
* @attribute
* @type {Enemy[]}
*/
enemies;
}
export { GameLogic };
With Enemy undefined (not declared, not imported, or a typo), parseAttributes() returns no attribute and no error; getAttributes() doesn't list the member either. Non-array @type {Enemy} behaves the same, as does @type {{}}.
The type resolves to TypeScript's error type, which has no symbol, so the if (!typeNode) continue at src/parsers/script-parser.ts:500 skips the member before any of the reporting paths below it run. An error type is detectable and could be reported with the existing "…" is not a valid attribute type message.
This is the one that makes a typo'd or missing Interface import hard to diagnose: the attribute just never appears in the Editor, with nothing in the editor's problem list to explain why.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/parsers/script-parser.ts at extractAttributes(), reached through parseAttributes(), and reproduce the three snippets with the playcanvas types loaded as described in test/utils.ts. Trace the neighboring ParsingError paths and add regression coverage for tuple types, empty object initializers, and unresolved @type references. Done means these cases no longer abort or silently disappear, while valid attributes continue to parse.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100