@playcanvas/attribute-parser 1.9.0 → 1.10.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/dist/index.js
CHANGED
|
@@ -417,20 +417,22 @@ var ts = /*#__PURE__*/_mergeNamespaces({
|
|
|
417
417
|
default: typescript
|
|
418
418
|
}, [typescriptExports]);
|
|
419
419
|
|
|
420
|
-
class ParsingError{constructor(node,message){this.node=node;this.message=message;}toString(){const file=this.node.getSourceFile();const fileName=file.fileName;const start=this.node.getSourceFile().getLineAndCharacterOfPosition(this.node.getStart());return `ParsingError: ${this.message} at ${fileName}:${start.line+1}:${start.character}`}}
|
|
420
|
+
class ParsingError{constructor(node,type,message,fix){this.node=node;this.type=type;this.message=message;this.fix=fix;}toString(){const file=this.node.getSourceFile();const fileName=file.fileName;const start=this.node.getSourceFile().getLineAndCharacterOfPosition(this.node.getStart());return `ParsingError: ${this.message} at ${fileName}:${start.line+1}:${start.character}`}}
|
|
421
421
|
|
|
422
|
-
const
|
|
422
|
+
const getTag=(tag,node)=>{for(const jsDoc of node?.jsDoc||[]){for(const tagNode of jsDoc.tags||[]){if(tagNode.tagName.text===tag){return tagNode}}}return null};const hasTag=(tag,node)=>{return !!getTag(tag,node)};const isStaticMember=member=>(typescriptExports.getCombinedModifierFlags(member)&typescriptExports.ModifierFlags.Static)!==0;
|
|
423
423
|
|
|
424
|
-
function createDefaultMapFromCDN(options,prefix,ts){const fsMap=new Map;const files=knownLibFilesForCompilerOptions(options,ts);async function uncached(){const contentPromises=files.map(lib=>{return fetch(prefix+lib).then(resp=>{if(!resp.ok){throw new Error(`Failed to fetch ${prefix+lib}: ${resp.statusText}`)}return resp.text()})});const contents=await Promise.all(contentPromises);contents.forEach((text,index)=>fsMap.set(`/${files[index]}`,text));return fsMap}return uncached()}function resolveAliasedSymbol(typeChecker,symbol){while(symbol.flags&typescriptExports.SymbolFlags.Alias){symbol=typeChecker.getAliasedSymbol(symbol);}return symbol}function getExportedNodes(program,sourceFile){if(!program||!sourceFile){return []}const typeChecker=program.getTypeChecker();const moduleSymbol=typeChecker.getSymbolAtLocation(sourceFile);if(!moduleSymbol){return []}const exportedSymbols=typeChecker.getExportsOfModule(moduleSymbol);const exportedNodes=new Map;exportedSymbols.forEach(symbol=>{const resolvedSymbol=resolveAliasedSymbol(typeChecker,symbol);if(resolvedSymbol.declarations){const node=resolvedSymbol.declarations[0];const name=symbol.name==="default"?node.name.getText():symbol.name;exportedNodes.set(name,node);}});return exportedNodes}function isAliasedClassDeclaration(node,typeChecker){if(typescriptExports.isClassDeclaration(node)){return true}if(node?.name&&typescriptExports.isIdentifier(node.name)){const symbol=typeChecker.getSymbolAtLocation(node.name);if(symbol){const resolvedSymbol=resolveAliasedSymbol(typeChecker,symbol);if(resolvedSymbol.declarations){return resolvedSymbol.declarations.some(typescriptExports.isClassDeclaration)}}}return false}function inheritsFrom(node,typeChecker,baseClassSymbol){if(node.heritageClauses){for(const clause of node.heritageClauses){for(const type of clause.types){const symbol=typeChecker.getTypeAtLocation(type.expression).getSymbol();if(symbol===baseClassSymbol){return true}if(symbol?.declarations.length>0){const declaration=symbol.declarations[0];if(inheritsFrom(declaration,typeChecker,baseClassSymbol)){return true}}}}}return false}function flatMapAnyNodes(node,filter=()=>true){const results=[];function visit(node){if(filter(node)){results.push(node);}typescriptExports.forEachChild(node,visit);}visit(node);return results}function getSuperClasses(node,typeChecker){const superClasses=[node];let currentClass=node;while(currentClass){const heritageClauses=currentClass.heritageClauses;if(!heritageClauses){break}const extendsClause=heritageClauses.find(clause=>clause.token===typescriptExports.SyntaxKind.ExtendsKeyword);if(!extendsClause||extendsClause.types.length===0){break}const superClassType=typeChecker.getTypeAtLocation(extendsClause.types[0]);const superClassSymbol=superClassType.getSymbol();if(!superClassSymbol){break}const superClassDeclaration=superClassSymbol.declarations.find(typescriptExports.isClassDeclaration);if(!superClassDeclaration){break}superClasses.push(superClassDeclaration);currentClass=superClassDeclaration;}return superClasses}function getJSDocTags(node,typeChecker){const tags=[];if(typescriptExports.isClassDeclaration(node)||typescriptExports.isInterfaceDeclaration(node)){const heritageChain=getSuperClasses(node,typeChecker).filter(classNode=>!classNode.getSourceFile().isDeclarationFile);heritageChain.forEach(classNode=>{classNode.members.forEach(member=>{const isSetter=typescriptExports.isSetAccessor(member);if(typescriptExports.isPropertyDeclaration(member)||isSetter||typescriptExports.isPropertySignature(member)){const memberName=member.name&&typescriptExports.isIdentifier(member.name)?member.name.text:"unnamed";let hasJsDoc=member.jsDoc?.length>0;if(isSetter&&!hasJsDoc){const symbol=typeChecker.getSymbolAtLocation(member.name);member=symbol.declarations.find(accessor=>accessor.jsDoc?.length>0);hasJsDoc=!!member;}if(hasJsDoc){tags.push({memberName,member});}}});});}else if(typescriptExports.isObjectLiteralExpression(node)){node.properties.forEach(property=>{if(typescriptExports.isPropertyAssignment(property)){const memberName=property.name&&(typescriptExports.isIdentifier(property.name)||typescriptExports.isStringLiteral(property.name))?property.name.text:"unnamed";if(property.jsDoc?.length>0){tags.push({memberName,member:property});}}});}return tags}function getPrimitiveEnumType(type,typeChecker){if(!type.symbol?.declarations?.some(decl=>typescriptExports.isEnumDeclaration(decl)))return null;const enumMembers=type.symbol.declarations[0].members;const firstMemberValue=typeChecker.getConstantValue(enumMembers[0]);const validEnumType=["number","string","boolean"];const typeOf=typeof firstMemberValue;return validEnumType.includes(typeOf)?typeOf:null}function getType(node,typeChecker){if(node?.name?.kind===typescriptExports.SyntaxKind.Identifier){const type=typeChecker.getTypeAtLocation(node);const array=typeChecker.isArrayType(type);const actualType=array?typeChecker.getElementTypeOfArrayType(type):type;const baseType=typeChecker.getBaseTypeOfLiteralType(actualType);let isMixedUnion=false;if(Array.isArray(baseType.types)){const primitiveKinds=new Set(baseType.types.map(t=>typeChecker.getBaseTypeOfLiteralType(t)));primitiveKinds.delete(null);isMixedUnion=primitiveKinds.size>1;}const name=getPrimitiveEnumType(baseType,typeChecker)??typeChecker.typeToString(baseType);return {type:baseType,name,array,isMixedUnion}}return null}function parseNumber(input){let cleanedInput=input.replace(/_/g,"").toLowerCase();let unaryOperator=null;if(cleanedInput.startsWith("+")||cleanedInput.startsWith("-")||cleanedInput.startsWith("~")){unaryOperator=cleanedInput[0];cleanedInput=cleanedInput.slice(1).trim();}let result;if(cleanedInput==="NaN"||cleanedInput===""){result=NaN;}else if(cleanedInput.startsWith("0b")){result=parseInt(cleanedInput.slice(2),2);}else if(cleanedInput.startsWith("0o")){result=parseInt(cleanedInput.slice(2),8);}else if(cleanedInput.startsWith("0x")){result=parseInt(cleanedInput.slice(2),16);}else if(cleanedInput.includes("e")){result=parseFloat(cleanedInput);}else if(!isNaN(Number(cleanedInput))){result=Number(cleanedInput);}else {result=NaN;}if(unaryOperator==="+"){result=+result;}else if(unaryOperator==="-"){result=-result;}else if(unaryOperator==="~"){result=~result;}return result}function parseStringToNumericalArray(input){const sanitizedInput=input.replace(/\s+/g,"");if(!sanitizedInput.startsWith("[")||!sanitizedInput.endsWith("]")){throw new Error("Invalid input string")}const arrayString=sanitizedInput.slice(1,-1);const elements=arrayString.split(",");const parsedArray=elements.map(element=>{return parseNumber(element.trim())});if(parsedArray.some(isNaN)){throw new Error("Parsed result contains invalid numbers")}return parsedArray}const isNumericLiteral=node=>{const isNumericLiteral=typescriptExports.isNumericLiteral(node);const isUnaryExpression=typescriptExports.isUnaryExpression(node)&&(node.operator===typescriptExports.SyntaxKind.MinusToken||node.operator===typescriptExports.SyntaxKind.PlusToken)&&node.operand.kind===typescriptExports.SyntaxKind.NumericLiteral;return isNumericLiteral||isUnaryExpression};const parseFloatNode=(node,isArgument=false)=>{if(!isNumericLiteral(node)){if(typescriptExports.isPropertyAssignment(node.parent)||typescriptExports.isPropertyDeclaration(node.parent)||isArgument){throw new ParsingError(node,`"${node.parent.getText()}" must be defined with a numeric literal.`)}return 0}return parseNumber(node.getText())};function resolveIdentifier(node,typeChecker){const symbol=typeChecker.getSymbolAtLocation(node);if(symbol&&symbol.declarations){for(const declaration of symbol.declarations){if(typescriptExports.isVariableDeclaration(declaration)&&declaration.initializer){return getLiteralValue(declaration.initializer,typeChecker)}}}return undefined}const resolvePropertyAccess=(node,typeChecker)=>{const symbol=typeChecker.getSymbolAtLocation(node);if(symbol&&symbol.declarations){for(const declaration of symbol.declarations){if(typescriptExports.isPropertyAssignment(declaration)&&declaration.initializer){return getLiteralValue(declaration.initializer,typeChecker)}if(typescriptExports.isVariableDeclaration(declaration)&&declaration.initializer){return getLiteralValue(declaration.initializer,typeChecker)}if(typescriptExports.isEnumMember(declaration)){return declaration.initializer?getLiteralValue(declaration.initializer,typeChecker):declaration.name.getText()}}}const objValue=getLiteralValue(node.expression,typeChecker);if(objValue&&typeof objValue==="object"){const propName=node.name.text;return objValue[propName]}return undefined};const evaluatePrefixUnaryExpression=(node,typeChecker)=>{const operandValue=getLiteralValue(node.operand,typeChecker);if(operandValue!==undefined){switch(node.operator){case typescriptExports.SyntaxKind.PlusToken:return +operandValue;case typescriptExports.SyntaxKind.MinusToken:return -operandValue;case typescriptExports.SyntaxKind.ExclamationToken:return !operandValue;case typescriptExports.SyntaxKind.TildeToken:return ~operandValue}}return undefined};function handleObjectLiteral(node,typeChecker){const obj={};node.properties.forEach(prop=>{if(typescriptExports.isPropertyAssignment(prop)){const key=prop.name.getText();const value=getLiteralValue(prop.initializer,typeChecker);obj[key]=value;}else if(typescriptExports.isShorthandPropertyAssignment(prop)){const key=prop.name.getText();const value=resolveIdentifier(prop.name,typeChecker);obj[key]=value;}});return obj}function getLiteralValue(node,typeChecker){if(!node)return undefined;if(typescriptExports.isLiteralExpression(node)||typescriptExports.isBooleanLiteral(node)){if(typescriptExports.isStringLiteral(node)){return node.text}if(typescriptExports.isNumericLiteral(node)){return Number(node.text)}if(node.kind===typescriptExports.SyntaxKind.TrueKeyword){return true}if(node.kind===typescriptExports.SyntaxKind.FalseKeyword){return false}}switch(node.kind){case typescriptExports.SyntaxKind.NullKeyword:return null;case typescriptExports.SyntaxKind.ArrayLiteralExpression:return node.elements.map(element=>getLiteralValue(element,typeChecker));case typescriptExports.SyntaxKind.ObjectLiteralExpression:return handleObjectLiteral(node,typeChecker);case typescriptExports.SyntaxKind.Identifier:return resolveIdentifier(node,typeChecker);case typescriptExports.SyntaxKind.PropertyAccessExpression:return resolvePropertyAccess(node,typeChecker);case typescriptExports.SyntaxKind.ParenthesizedExpression:return getLiteralValue(node.expression,typeChecker);case typescriptExports.SyntaxKind.PrefixUnaryExpression:return evaluatePrefixUnaryExpression(node,typeChecker);default:return undefined}}const parseArrayLiteralNode=(node,isArgument=false)=>{if(node.kind!==typescriptExports.SyntaxKind.ArrayLiteralExpression){if(typescriptExports.isPropertyAssignment(node.parent)||typescriptExports.isPropertyDeclaration(node.parent)||isArgument){throw new ParsingError(node,`"${node.parent.getText()}" must be defined with an array literal.`)}return []}return JSON.parse(node.getText())};const parseArrayLiteral=args=>{if(args.length>0){return parseArrayLiteralNode(args[0],true)}return []};
|
|
424
|
+
function createDefaultMapFromCDN(options,prefix,ts){const fsMap=new Map;const files=knownLibFilesForCompilerOptions(options,ts);async function uncached(){const contentPromises=files.map(lib=>{return fetch(prefix+lib).then(resp=>{if(!resp.ok){throw new Error(`Failed to fetch ${prefix+lib}: ${resp.statusText}`)}return resp.text()})});const contents=await Promise.all(contentPromises);contents.forEach((text,index)=>fsMap.set(`/${files[index]}`,text));return fsMap}return uncached()}function resolveAliasedSymbol(typeChecker,symbol){while(symbol.flags&typescriptExports.SymbolFlags.Alias){symbol=typeChecker.getAliasedSymbol(symbol);}return symbol}function getExportedNodes(program,sourceFile){if(!program||!sourceFile){return []}const typeChecker=program.getTypeChecker();const moduleSymbol=typeChecker.getSymbolAtLocation(sourceFile);if(!moduleSymbol){return []}const exportedSymbols=typeChecker.getExportsOfModule(moduleSymbol);const exportedNodes=new Map;exportedSymbols.forEach(symbol=>{const resolvedSymbol=resolveAliasedSymbol(typeChecker,symbol);if(resolvedSymbol.declarations){const node=resolvedSymbol.declarations[0];const name=symbol.name==="default"?node.name.getText():symbol.name;exportedNodes.set(name,node);}});return exportedNodes}function isAliasedClassDeclaration(node,typeChecker){if(typescriptExports.isClassDeclaration(node)){return true}if(node?.name&&typescriptExports.isIdentifier(node.name)){const symbol=typeChecker.getSymbolAtLocation(node.name);if(symbol){const resolvedSymbol=resolveAliasedSymbol(typeChecker,symbol);if(resolvedSymbol.declarations){return resolvedSymbol.declarations.some(typescriptExports.isClassDeclaration)}}}return false}function inheritsFrom(node,typeChecker,baseClassSymbol){if(node.heritageClauses){for(const clause of node.heritageClauses){for(const type of clause.types){const symbol=typeChecker.getTypeAtLocation(type.expression).getSymbol();if(symbol===baseClassSymbol){return true}if(symbol?.declarations.length>0){const declaration=symbol.declarations[0];if(inheritsFrom(declaration,typeChecker,baseClassSymbol)){return true}}}}}return false}function flatMapAnyNodes(node,filter=()=>true){const results=[];function visit(node){if(filter(node)){results.push(node);}typescriptExports.forEachChild(node,visit);}visit(node);return results}function getSuperClasses(node,typeChecker){const superClasses=[node];let currentClass=node;while(currentClass){const heritageClauses=currentClass.heritageClauses;if(!heritageClauses){break}const extendsClause=heritageClauses.find(clause=>clause.token===typescriptExports.SyntaxKind.ExtendsKeyword);if(!extendsClause||extendsClause.types.length===0){break}const superClassType=typeChecker.getTypeAtLocation(extendsClause.types[0]);const superClassSymbol=superClassType.getSymbol();if(!superClassSymbol){break}const superClassDeclaration=superClassSymbol.declarations.find(typescriptExports.isClassDeclaration);if(!superClassDeclaration){break}superClasses.push(superClassDeclaration);currentClass=superClassDeclaration;}return superClasses}function getJSDocTags(node,typeChecker){const tags=[];if(typescriptExports.isClassDeclaration(node)||typescriptExports.isInterfaceDeclaration(node)){const heritageChain=getSuperClasses(node,typeChecker).filter(classNode=>!classNode.getSourceFile().isDeclarationFile);heritageChain.forEach(classNode=>{classNode.members.forEach(member=>{const isSetter=typescriptExports.isSetAccessor(member);if(typescriptExports.isPropertyDeclaration(member)||isSetter||typescriptExports.isPropertySignature(member)){const memberName=member.name&&typescriptExports.isIdentifier(member.name)?member.name.text:"unnamed";let hasJsDoc=member.jsDoc?.length>0;if(isSetter&&!hasJsDoc){const symbol=typeChecker.getSymbolAtLocation(member.name);member=symbol.declarations.find(accessor=>accessor.jsDoc?.length>0);hasJsDoc=!!member;}if(hasJsDoc){tags.push({memberName,member});}}});});}else if(typescriptExports.isObjectLiteralExpression(node)){node.properties.forEach(property=>{if(typescriptExports.isPropertyAssignment(property)){const memberName=property.name&&(typescriptExports.isIdentifier(property.name)||typescriptExports.isStringLiteral(property.name))?property.name.text:"unnamed";if(property.jsDoc?.length>0){tags.push({memberName,member:property});}}});}return tags}function getPrimitiveEnumType(type,typeChecker){if(!type.symbol?.declarations?.some(decl=>typescriptExports.isEnumDeclaration(decl)))return null;const enumMembers=type.symbol.declarations[0].members;const firstMemberValue=typeChecker.getConstantValue(enumMembers[0]);const validEnumType=["number","string","boolean"];const typeOf=typeof firstMemberValue;return validEnumType.includes(typeOf)?typeOf:null}function getType(node,typeChecker){if(node?.name?.kind===typescriptExports.SyntaxKind.Identifier){const type=typeChecker.getTypeAtLocation(node);const array=typeChecker.isArrayType(type);const actualType=array?typeChecker.getElementTypeOfArrayType(type):type;const baseType=typeChecker.getBaseTypeOfLiteralType(actualType);let isMixedUnion=false;if(Array.isArray(baseType.types)){const primitiveKinds=new Set(baseType.types.map(t=>typeChecker.getBaseTypeOfLiteralType(t)));primitiveKinds.delete(null);isMixedUnion=primitiveKinds.size>1;}const name=getPrimitiveEnumType(baseType,typeChecker)??typeChecker.typeToString(baseType);return {type:baseType,name,array,isMixedUnion}}return null}function parseNumber(input){let cleanedInput=input.replace(/_/g,"").toLowerCase();let unaryOperator=null;if(cleanedInput.startsWith("+")||cleanedInput.startsWith("-")||cleanedInput.startsWith("~")){unaryOperator=cleanedInput[0];cleanedInput=cleanedInput.slice(1).trim();}let result;if(cleanedInput==="NaN"||cleanedInput===""){result=NaN;}else if(cleanedInput.startsWith("0b")){result=parseInt(cleanedInput.slice(2),2);}else if(cleanedInput.startsWith("0o")){result=parseInt(cleanedInput.slice(2),8);}else if(cleanedInput.startsWith("0x")){result=parseInt(cleanedInput.slice(2),16);}else if(cleanedInput.includes("e")){result=parseFloat(cleanedInput);}else if(!isNaN(Number(cleanedInput))){result=Number(cleanedInput);}else {result=NaN;}if(unaryOperator==="+"){result=+result;}else if(unaryOperator==="-"){result=-result;}else if(unaryOperator==="~"){result=~result;}return result}function parseStringToNumericalArray(input){const sanitizedInput=input.replace(/\s+/g,"");if(!sanitizedInput.startsWith("[")||!sanitizedInput.endsWith("]")){throw new Error("Invalid input string")}const arrayString=sanitizedInput.slice(1,-1);const elements=arrayString.split(",");const parsedArray=elements.map(element=>{return parseNumber(element.trim())});if(parsedArray.some(isNaN)){throw new Error("Parsed result contains invalid numbers")}return parsedArray}const isNumericLiteral=node=>{const isNumericLiteral=typescriptExports.isNumericLiteral(node);const isUnaryExpression=typescriptExports.isUnaryExpression(node)&&(node.operator===typescriptExports.SyntaxKind.MinusToken||node.operator===typescriptExports.SyntaxKind.PlusToken)&&node.operand.kind===typescriptExports.SyntaxKind.NumericLiteral;return isNumericLiteral||isUnaryExpression};const parseFloatNode=(node,isArgument=false)=>{if(!isNumericLiteral(node)){if(typescriptExports.isPropertyAssignment(node.parent)||typescriptExports.isPropertyDeclaration(node.parent)||isArgument){throw new ParsingError(node,"Not a Literal",`"${node.parent.getText()}" must be defined with a numeric literal.`)}return 0}return parseNumber(node.getText())};function resolveIdentifier(node,typeChecker){const symbol=typeChecker.getSymbolAtLocation(node);if(symbol&&symbol.declarations){for(const declaration of symbol.declarations){if(typescriptExports.isVariableDeclaration(declaration)&&declaration.initializer){return getLiteralValue(declaration.initializer,typeChecker)}}}return undefined}const resolvePropertyAccess=(node,typeChecker)=>{const symbol=typeChecker.getSymbolAtLocation(node);if(symbol&&symbol.declarations){for(const declaration of symbol.declarations){if(typescriptExports.isPropertyAssignment(declaration)&&declaration.initializer){return getLiteralValue(declaration.initializer,typeChecker)}if(typescriptExports.isVariableDeclaration(declaration)&&declaration.initializer){return getLiteralValue(declaration.initializer,typeChecker)}if(typescriptExports.isEnumMember(declaration)){return declaration.initializer?getLiteralValue(declaration.initializer,typeChecker):declaration.name.getText()}}}const objValue=getLiteralValue(node.expression,typeChecker);if(objValue&&typeof objValue==="object"){const propName=node.name.text;return objValue[propName]}return undefined};const evaluatePrefixUnaryExpression=(node,typeChecker)=>{const operandValue=getLiteralValue(node.operand,typeChecker);if(operandValue!==undefined){switch(node.operator){case typescriptExports.SyntaxKind.PlusToken:return +operandValue;case typescriptExports.SyntaxKind.MinusToken:return -operandValue;case typescriptExports.SyntaxKind.ExclamationToken:return !operandValue;case typescriptExports.SyntaxKind.TildeToken:return ~operandValue}}return undefined};function handleObjectLiteral(node,typeChecker){const obj={};node.properties.forEach(prop=>{if(typescriptExports.isPropertyAssignment(prop)){const key=prop.name.getText();const value=getLiteralValue(prop.initializer,typeChecker);obj[key]=value;}else if(typescriptExports.isShorthandPropertyAssignment(prop)){const key=prop.name.getText();const value=resolveIdentifier(prop.name,typeChecker);obj[key]=value;}});return obj}function getLiteralValue(node,typeChecker){if(!node)return undefined;if(typescriptExports.isLiteralExpression(node)||typescriptExports.isBooleanLiteral(node)){if(typescriptExports.isStringLiteral(node)){return node.text}if(typescriptExports.isNumericLiteral(node)){return Number(node.text)}if(node.kind===typescriptExports.SyntaxKind.TrueKeyword){return true}if(node.kind===typescriptExports.SyntaxKind.FalseKeyword){return false}}switch(node.kind){case typescriptExports.SyntaxKind.NullKeyword:return null;case typescriptExports.SyntaxKind.ArrayLiteralExpression:return node.elements.map(element=>getLiteralValue(element,typeChecker));case typescriptExports.SyntaxKind.ObjectLiteralExpression:return handleObjectLiteral(node,typeChecker);case typescriptExports.SyntaxKind.Identifier:return resolveIdentifier(node,typeChecker);case typescriptExports.SyntaxKind.PropertyAccessExpression:return resolvePropertyAccess(node,typeChecker);case typescriptExports.SyntaxKind.ParenthesizedExpression:return getLiteralValue(node.expression,typeChecker);case typescriptExports.SyntaxKind.PrefixUnaryExpression:return evaluatePrefixUnaryExpression(node,typeChecker);default:return undefined}}const parseArrayLiteralNode=(node,isArgument=false)=>{if(node.kind!==typescriptExports.SyntaxKind.ArrayLiteralExpression){if(typescriptExports.isPropertyAssignment(node.parent)||typescriptExports.isPropertyDeclaration(node.parent)||isArgument){throw new ParsingError(node,"Not a Literal",`"${node.parent.getText()}" must be defined with an array literal.`)}return []}return JSON.parse(node.getText())};const parseArrayLiteral=args=>{if(args.length>0){return parseArrayLiteralNode(args[0],true)}return []};
|
|
425
425
|
|
|
426
426
|
function parseTag(input=""){input=input.replace(/['`]/g,'"').trim();if(input==="true")return true;if(input==="false")return false;const num=parseNumber(input);if(!isNaN(num))return num;try{const arr=parseStringToNumericalArray(input);if(Array.isArray(arr))return arr}catch{}try{const arr=JSON.parse(input);if(Array.isArray(arr))return arr}catch{}return input}function validateTag(value,typeAnnotation,env){const virtualFileName="/___virtual__.ts";const sourceText=`let a: ${typeAnnotation} = ${value};`;env.createFile(virtualFileName,sourceText);const languageService=env.languageService;const errors=languageService.getSemanticDiagnostics(virtualFileName);const typeErrors=errors.filter(error=>error.code===2322&&error.category===typescriptExports.DiagnosticCategory.Error);if(typeErrors.length>0){const errorMessage=typeErrors[0].messageText.toString();throw new Error(`Type Validation Error: ${errorMessage}`)}return true}
|
|
427
427
|
|
|
428
|
-
class AttributeParser{constructor(tags=new Map,env,typeSerializerMap){this.env=env;this.tagTypeAnnotations=tags;this.typeSerializerMap=typeSerializerMap;this.program=env.languageService.getProgram();this.typeChecker=this.program.getTypeChecker();}parseAttributeComment(node,errors=[],requiresAttributeTag=true){if(!requiresAttributeTag||hasTag("attribute",node)){const attribute=this.getNodeAsAttribute(node,errors);if(!attribute)return;attribute.description=node.jsDoc?.[0]?.comment||"";const jsDocTags=node.jsDoc?.[0]?.tags??[];jsDocTags.forEach(tag=>{let commentText=(tag.comment?.split("\n")[0]||"").trim();if(this.tagTypeAnnotations.has(
|
|
428
|
+
class AttributeParser{constructor(tags=new Map,env,typeSerializerMap){this.env=env;this.tagTypeAnnotations=tags;this.typeSerializerMap=typeSerializerMap;this.program=env.languageService.getProgram();this.typeChecker=this.program.getTypeChecker();}parseAttributeComment(node,errors=[],requiresAttributeTag=true){if(!requiresAttributeTag||hasTag("attribute",node)){const attribute=this.getNodeAsAttribute(node,errors);if(!attribute)return;attribute.description=node.jsDoc?.[0]?.comment||"";const jsDocTags=node.jsDoc?.[0]?.tags??[];jsDocTags.forEach(tag=>{const tagName=tag.tagName.text;let commentText=(tag.comment?.split("\n")[0]||"").trim();if(this.tagTypeAnnotations.has(tagName)){const{type,supportMessage,fix}=this.tagTypeAnnotations.get(tagName);try{const value=parseTag(commentText);if(typeof value==="string")commentText=`"${commentText}"`;validateTag(commentText,type,this.env);attribute[tagName]=value;}catch(error){let startPos=tag.getStart();let endPos=tag.getEnd();const fullText=tag.getText();const commentText=tag.comment?.split("\n")[0]?.trim()||"";const commentStart=fullText.indexOf(commentText);if(commentStart!==-1){startPos=tag.getStart()+commentStart;endPos=startPos+commentText.length;}const sourceFile=tag.getSourceFile();const startLineChar=sourceFile.getLineAndCharacterOfPosition(startPos);const endLineChar=sourceFile.getLineAndCharacterOfPosition(endPos);const edit=fix?{text:fix,title:`Fix @${tagName} tag`,range:{startLineNumber:startLineChar.line+1,startColumn:startLineChar.character+1,endLineNumber:endLineChar.line+1,endColumn:endLineChar.character+1}}:null;const errorMessage=supportMessage?.()??"The tag is invalid.";const parseError=new ParsingError(tag,`Invalid Tag '@${tagName}'`,errorMessage,edit);errors.push(parseError);}}});return attribute}}getNodeAsAttribute(node,errors=[]){const name=node.name&&typescriptExports.isIdentifier(node.name)&&node.name.text;const{type,name:typeName,array,isMixedUnion}=getType(node,this.typeChecker);const enums=this.getEnumMembers(node,errors);let value=null;if(isMixedUnion&&enums.length===0){errors.push(new ParsingError(node,"Invalid Type","Mixed literal union types (combining different primitive types like string | number) are not supported. "+'Use a union of the same primitive type (e.g., \'1 | 2 | 3\' or \'"a" | "b" | "c"\') or refactor your type.'));return}const serializer=!array&&this.typeSerializerMap.get(typeName);if(serializer){try{value=serializer(node.initializer??node,this.typeChecker);}catch(error){errors.push(error);return}}return {type,name,typeName,array,value,enum:enums}}getEnumMembers(node,errors){let typeNode=null;let members=[];if(node.type){typeNode=node;}else {const jsDocs=typescriptExports.getJSDocTags(node);const typeTag=jsDocs.find(doc=>doc.tagName.text==="type");if(typeTag&&typescriptExports.isJSDocTypeTag(typeTag)){typeNode=typeTag.typeExpression;}}const type=typeNode&&(typeNode.type.elementType??typeNode.type);if(typeNode&&typescriptExports.isTypeReferenceNode(type)){let symbol=this.typeChecker.getSymbolAtLocation(type.typeName);if(symbol&&symbol.flags&typescriptExports.SymbolFlags.Alias){symbol=this.typeChecker.getAliasedSymbol(symbol);}if(symbol){const declarations=symbol.getDeclarations();if(declarations&&declarations.length>0){declarations.forEach(declaration=>{if(typescriptExports.isEnumDeclaration(declaration)){members=declaration.members.map(member=>({[member.name.text]:member.initializer.text}));}const jsDocs=typescriptExports.getJSDocTags(declaration);const enumTag=jsDocs.find(doc=>doc.tagName.text==="enum");if(enumTag&&typescriptExports.isJSDocEnumTag(enumTag)){members=members.concat(this.getMembers(declaration.initializer,errors));}});}}}return members}getMembers(node){const members=[];if(typescriptExports.isObjectLiteralExpression(node)){node.properties.forEach(property=>{if(typescriptExports.isPropertyAssignment(property)){const isValidName=typescriptExports.isIdentifier(property.name)||typescriptExports.isStringLiteral(property.name);const name=isValidName&&property.name.text;const value=getLiteralValue(property.initializer,this.typeChecker);members.push({[name]:value});}});}return members}}
|
|
429
429
|
|
|
430
430
|
function zipArrays(a,b){const length=Math.max(a.length,b.length);const result=new Array(length);for(let i=0;i<length;i++){result[i]=i<a.length?a[i]:b[i];}return result}
|
|
431
431
|
|
|
432
|
-
const createNewExpressionParser=(name,argProcessor,defaultArr=[])=>{return node=>{if(node.kind!==typescriptExports.SyntaxKind.NewExpression){if(typescriptExports.isPropertyAssignment(node.parent)||typescriptExports.isPropertyDeclaration(node.parent)){throw new ParsingError(node,`Property "${node.name.text}" is not a ${name}.`)}return defaultArr}const args=argProcessor(node.arguments);return zipArrays(args,defaultArr)}};const parseNumberArguments=args=>args.map(arg=>parseFloatNode(arg,true));const createNumberArgumentParser=(name,defaultArr=[])=>createNewExpressionParser(name,parseNumberArguments,defaultArr);const createArrayArgumentParser=(name,defaultArr=[])=>createNewExpressionParser(name,parseArrayLiteral,defaultArr);const SUPPORTED_INITIALIZABLE_TYPE_NAMES=new Map([["Curve",createArrayArgumentParser("Curve")],["Vec2",createNumberArgumentParser("Vec2",[0,0])],["Vec3",createNumberArgumentParser("Vec3",[0,0,0])],["Vec4",createNumberArgumentParser("Vec4",[0,0,0,0])],["Color",createNumberArgumentParser("Color",[1,1,1,1])],["number",getLiteralValue],["string",getLiteralValue],["boolean",getLiteralValue]]);const SUPPORTED_PLAYCANVAS_TYPES=new Set(["Curve","Asset","Entity","Vec2","Vec3","Vec4","Color"]);const isSupportedPlayCanvasNode=node=>{return typescriptExports.isClassDeclaration(node)&&SUPPORTED_PLAYCANVAS_TYPES.has(node.name.text)};const EDITOR_ASSET_TYPES=["animation","animstategraph","audio","bundle","binary","container","cubemap","css","font","folder","json","html","gsplat","material","model","render","scene","script","shader","sprite","template","text","texture","textureAtlas","wasm"];const resourceTypesAsUnion=EDITOR_ASSET_TYPES.map(type=>`"${type}"`).join("|");const SUPPORTED_BLOCK_TAGS=new Map([["resource",resourceTypesAsUnion],["color",'"r"|"rg"|"rgb"|"rgba"'],["curves",'["x"] | ["x", "y"] | ["x", "y", "z"] | ["x", "z"] | ["y"] | ["y", "z"] | ["z"]'],["range","number[]"],["placeholder","string"],["precision","number"],["size","number"],["step","number"],["title","string"],["default","any"],["enabledif","any"],["visibleif","any"]]);const mapAttributesToOutput=attribute=>{attribute.type=attribute.typeName.toLowerCase();if(attribute.range){if(attribute.range.length>0){attribute.min=attribute.range[0];if(attribute.range.length>1){attribute.max=attribute.range[1];}}delete attribute.range;}if(typeof attribute.attributes=="object"){attribute.type="json";attribute.schema=Object.keys(attribute.attributes).map(key=>{return attribute.attributes[key]});delete attribute.attributes;}if(attribute.type==="color"){attribute.type="rgba";}if(attribute.resource){attribute.assetType=attribute.resource;delete attribute.resource;}if(attribute.enum.length===0)delete attribute.enum;if(attribute.value===undefined){if(attribute.type==="string")attribute.value="";if(attribute.type==="number")attribute.value=0;if(attribute.type==="boolean")attribute.value=false;}if(attribute.value!==undefined)attribute.default=attribute.default??attribute.value;if(attribute.type==="curve"&&Array.isArray(attribute.value)&&attribute.value.length===0){delete attribute.default;}if(attribute.enabledif?.startsWith("{")&&attribute.enabledif?.endsWith("}")){attribute.enabledif=attribute.enabledif.slice(1,-1);}if(attribute.visibleif?.startsWith("{")&&attribute.visibleif?.endsWith("}")){attribute.visibleif=attribute.visibleif.slice(1,-1);}delete attribute.typeName;delete attribute.value;return attribute};class ScriptParser{constructor(env,maxDepth=2){if(!env){throw new Error("A TypeScript Environment must be provided to the ScriptParser.")}this.env=env;this.program=env.languageService.getProgram();this.typeChecker=this.program.getTypeChecker();this.maxDepth=maxDepth;const playcanvasTypesFile=this.program.getSourceFile("./playcanvas.d.ts");if(!playcanvasTypesFile){throw new Error("The PlayCanvas types file could not be found.")}const allSupportedTypes=flatMapAnyNodes(playcanvasTypesFile,isSupportedPlayCanvasNode).map(node=>this.typeChecker.getTypeAtLocation(node));allSupportedTypes.push(this.typeChecker.getStringType());allSupportedTypes.push(this.typeChecker.getNumberType());allSupportedTypes.push(this.typeChecker.getBooleanType());this.typeSerializerMap=new Map;this.allSupportedTypesSet=new Set(allSupportedTypes);allSupportedTypes.forEach(type=>{const typeName=this.typeChecker.typeToString(type);const serializer=SUPPORTED_INITIALIZABLE_TYPE_NAMES.get(typeName);if(serializer){this.typeSerializerMap.set(typeName,serializer);}});this.attributeParser=new AttributeParser(SUPPORTED_BLOCK_TAGS,env,this.typeSerializerMap);}validateProgram(errors=[]){const compilerDiagnostics=this.program.getSyntacticDiagnostics();if(compilerDiagnostics.length>0){for(const diagnostic of compilerDiagnostics){const message=typescriptExports.flattenDiagnosticMessageText(diagnostic.messageText,"\n");if(diagnostic.file){const location=diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);errors.push({message,file:diagnostic.file.fileName,line:location.line+1,column:location.character+1,length:diagnostic.length});}else {errors.push({message});}}}return errors.length===0}extractAttributes(node,opts={}){const attributes=opts.attributes||{};const errors=opts.errors??[];const requiresAttributeTag=opts.requiresAttributeTag??true;const depth=opts.depth??0;if(depth>10){return attributes}const members=getJSDocTags(node,this.typeChecker);for(const{member,memberName}of members){const attributeMetadata=this.attributeParser.parseAttributeComment(member,errors,requiresAttributeTag);if(attributeMetadata){const{type,typeName}=attributeMetadata;const initializer=member?.initializer;if(typeName==="any"){const
|
|
432
|
+
const createNewExpressionParser=(name,argProcessor,defaultArr=[])=>{return node=>{if(node.kind!==typescriptExports.SyntaxKind.NewExpression){if(typescriptExports.isPropertyAssignment(node.parent)||typescriptExports.isPropertyDeclaration(node.parent)){throw new ParsingError(node,"Invalid Property",`Property "${node.name.text}" is not a ${name}.`)}return defaultArr}const args=argProcessor(node.arguments);return zipArrays(args,defaultArr)}};const parseNumberArguments=args=>args.map(arg=>parseFloatNode(arg,true));const createNumberArgumentParser=(name,defaultArr=[])=>createNewExpressionParser(name,parseNumberArguments,defaultArr);const createArrayArgumentParser=(name,defaultArr=[])=>createNewExpressionParser(name,parseArrayLiteral,defaultArr);const SUPPORTED_INITIALIZABLE_TYPE_NAMES=new Map([["Curve",createArrayArgumentParser("Curve")],["Vec2",createNumberArgumentParser("Vec2",[0,0])],["Vec3",createNumberArgumentParser("Vec3",[0,0,0])],["Vec4",createNumberArgumentParser("Vec4",[0,0,0,0])],["Color",createNumberArgumentParser("Color",[1,1,1,1])],["number",getLiteralValue],["string",getLiteralValue],["boolean",getLiteralValue]]);const SUPPORTED_PLAYCANVAS_TYPES=new Set(["Curve","Asset","Entity","Vec2","Vec3","Vec4","Color"]);const isSupportedPlayCanvasNode=node=>{return typescriptExports.isClassDeclaration(node)&&SUPPORTED_PLAYCANVAS_TYPES.has(node.name.text)};const EDITOR_ASSET_TYPES=["animation","animstategraph","audio","bundle","binary","container","cubemap","css","font","folder","json","html","gsplat","material","model","render","scene","script","shader","sprite","template","text","texture","textureAtlas","wasm"];const resourceTypesAsUnion=EDITOR_ASSET_TYPES.map(type=>`"${type}"`).join("|");const SUPPORTED_BLOCK_TAGS=new Map([["resource",{type:resourceTypesAsUnion,supportMessage:()=>`@resource is malformed or invalid. The tag should be formatted as "@resource ${resourceTypesAsUnion}".`,fix:"texture"}],["color",{type:'"r"|"rg"|"rgb"|"rgba"',supportMessage:()=>'@color is malformed or invalid. The tag should be formatted as "@color r", "@color rg", "@color rgb", or "@color rgba".',fix:"rgb"}],["curves",{type:'["x"] | ["x", "y"] | ["x", "y", "z"] | ["x", "z"] | ["y"] | ["y", "z"] | ["z"]',supportMessage:()=>'@curves is malformed or invalid. The tag should be formatted as "@curves x", "@curves x y", "@curves x y z", "@curves x z", "@curves y", "@curves y z", or "@curves z".',fix:'["x"]'}],["range",{type:"number[]",supportMessage:()=>'@range is malformed or invalid. The tag should be formatted as "@range [0, 100]".',fix:"[0, 100]"}],["placeholder",{type:"string",supportMessage:()=>'@placeholder is malformed or invalid. The tag should be formatted as "@placeholder "My Placeholder"".',fix:'"My Placeholder"'}],["precision",{type:"number",supportMessage:()=>'@precision is malformed or invalid. The tag should be formatted as "@precision 0.1".',fix:"0.1"}],["size",{type:"number",supportMessage:()=>'@size is malformed or invalid. The tag should be formatted as "@size 100".',fix:"100"}],["step",{type:"number",supportMessage:()=>'@step is malformed or invalid. The tag should be formatted as "@step 1".',fix:"1"}],["title",{type:"string",supportMessage:()=>'@title is malformed or invalid. The tag should be formatted as "@title "My Attribute"".',fix:'"My Attribute"'}],["default",{type:"any"}],["enabledif",{type:"any"}],["visibleif",{type:"any"}]]);const mapAttributesToOutput=attribute=>{attribute.type=attribute.typeName.toLowerCase();if(attribute.range){if(attribute.range.length>0){attribute.min=attribute.range[0];if(attribute.range.length>1){attribute.max=attribute.range[1];}}delete attribute.range;}if(typeof attribute.attributes=="object"){attribute.type="json";attribute.schema=Object.keys(attribute.attributes).map(key=>{return attribute.attributes[key]});delete attribute.attributes;}if(attribute.type==="color"){attribute.type="rgba";}if(attribute.resource){attribute.assetType=attribute.resource;delete attribute.resource;}if(attribute.enum.length===0)delete attribute.enum;if(attribute.value===undefined){if(attribute.type==="string")attribute.value="";if(attribute.type==="number")attribute.value=0;if(attribute.type==="boolean")attribute.value=false;}if(attribute.value!==undefined)attribute.default=attribute.default??attribute.value;if(attribute.type==="curve"&&Array.isArray(attribute.value)&&attribute.value.length===0){delete attribute.default;}if(attribute.enabledif?.startsWith("{")&&attribute.enabledif?.endsWith("}")){attribute.enabledif=attribute.enabledif.slice(1,-1);}if(attribute.visibleif?.startsWith("{")&&attribute.visibleif?.endsWith("}")){attribute.visibleif=attribute.visibleif.slice(1,-1);}delete attribute.typeName;delete attribute.value;return attribute};class ScriptParser{constructor(env,maxDepth=2){if(!env){throw new Error("A TypeScript Environment must be provided to the ScriptParser.")}this.env=env;this.program=env.languageService.getProgram();this.typeChecker=this.program.getTypeChecker();this.maxDepth=maxDepth;const playcanvasTypesFile=this.program.getSourceFile("./playcanvas.d.ts");if(!playcanvasTypesFile){throw new Error("The PlayCanvas types file could not be found.")}const allSupportedTypes=flatMapAnyNodes(playcanvasTypesFile,isSupportedPlayCanvasNode).map(node=>this.typeChecker.getTypeAtLocation(node));allSupportedTypes.push(this.typeChecker.getStringType());allSupportedTypes.push(this.typeChecker.getNumberType());allSupportedTypes.push(this.typeChecker.getBooleanType());this.typeSerializerMap=new Map;this.allSupportedTypesSet=new Set(allSupportedTypes);allSupportedTypes.forEach(type=>{const typeName=this.typeChecker.typeToString(type);const serializer=SUPPORTED_INITIALIZABLE_TYPE_NAMES.get(typeName);if(serializer){this.typeSerializerMap.set(typeName,serializer);}});this.attributeParser=new AttributeParser(SUPPORTED_BLOCK_TAGS,env,this.typeSerializerMap);}validateProgram(errors=[]){const compilerDiagnostics=this.program.getSyntacticDiagnostics();if(compilerDiagnostics.length>0){for(const diagnostic of compilerDiagnostics){const message=typescriptExports.flattenDiagnosticMessageText(diagnostic.messageText,"\n");if(diagnostic.file){const location=diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);errors.push({message,file:diagnostic.file.fileName,line:location.line+1,column:location.character+1,length:diagnostic.length});}else {errors.push({message});}}}return errors.length===0}extractAttributes(node,opts={}){const attributes=opts.attributes||{};const errors=opts.errors??[];const requiresAttributeTag=opts.requiresAttributeTag??true;const depth=opts.depth??0;if(depth>10){return attributes}const members=getJSDocTags(node,this.typeChecker);for(const{member,memberName}of members){const attributeMetadata=this.attributeParser.parseAttributeComment(member,errors,requiresAttributeTag);if(attributeMetadata){const{type,typeName}=attributeMetadata;const initializer=member?.initializer;if(typeName==="any"){const supportedTypes=Array.from(SUPPORTED_INITIALIZABLE_TYPE_NAMES.keys()).join(", ");let errorMessage;if(member.initializer){errorMessage=`This attribute is initialized with an invalid type. An attribute should be one of the following: ${supportedTypes}.`;}else {errorMessage=`This attribute has an invalid @type tag. An attribute should be one of the following: ${supportedTypes}.`;}const error=new ParsingError(member,"Invalid Type",errorMessage);errors.push(error);continue}else if(!this.allSupportedTypesSet.has(type)&&attributeMetadata.enum.length===0){const symbol=type.aliasSymbol||type.symbol;const typeNode=symbol?.valueDeclaration||symbol.declarations?.[0];if(!typeNode){continue}const typeIsJSDocTypeDef=typescriptExports.isJSDocTypedefTag(typeNode);const typeIsInterface=hasTag("interface",typeNode)||typescriptExports.isInterfaceDeclaration(typeNode);const isInitialized=!!initializer&&typescriptExports.isObjectLiteralExpression(initializer);const isJSDocTypeLiteral=type.symbol.name==="__type";if(!isInitialized&&!typeIsInterface&&!typeIsJSDocTypeDef&&!isJSDocTypeLiteral){let error;const typeTag=getTag("type",member);const supportedTypes=Array.from(SUPPORTED_INITIALIZABLE_TYPE_NAMES.keys()).join(", ");if(typeTag){error=new ParsingError(typeTag.typeExpression,"Invalid Type",`"${typeTag.typeExpression.getText()}" is not a valid attribute type. An attribute should be one of the following: ${supportedTypes}`);}else if(initializer){error=new ParsingError(initializer,"Invalid Type",`"${initializer.getText()}" is not a valid attribute type. An attribute should be one of the following: ${supportedTypes}`);}else {error=new ParsingError(member,"Invalid Type",`Attribute is an invalid type. An attribute should be one of the following: ${supportedTypes}`);}errors.push(error);continue}const members=typeNode.members??typeNode.properties??Array.from(type.symbol.members).map(arr=>arr[1].declarations[0])??[];let commentAttributes;if(typeIsInterface||isInitialized){commentAttributes=this.extractAttributes(typeNode,{errors,requiresAttributeTag:false,depth:depth+1});}const nestedAttributes=members.reduce((attributes,prop)=>{const attribute=this.attributeParser.getNodeAsAttribute(prop,errors);if(attribute&&this.allSupportedTypesSet.has(attribute.type)){attributes[attribute.name]=mapAttributesToOutput(attribute);}return attributes},{});const mergedAttributes={...nestedAttributes,...commentAttributes};attributeMetadata.attributes=mergedAttributes;}attributes[memberName]=mapAttributesToOutput(attributeMetadata);}}return attributes}}
|
|
433
433
|
|
|
434
|
-
const toLowerCamelCase=str=>str[0].toLowerCase()+str.substring(1);const COMPILER_OPTIONS={noLib:true,strict:false,skipLibCheck:true,target:typescriptExports.ScriptTarget.ES2023,module:typescriptExports.ModuleKind.CommonJS,checkJs:true,allowJs:true,baseUrl:"./",paths:{"playcanvas":["/playcanvas.js"]}};class JSDocParser{async init(libPath){if(this._env){return this}let fsMap;if(!libPath){fsMap=await createDefaultMapFromNodeModules();}else if(libPath.endsWith(".d.ts")){const types=await fetch(libPath).then(r=>r.text());fsMap=new Map([["/lib.d.ts",types]]);}else {fsMap=await createDefaultMapFromCDN(COMPILER_OPTIONS,libPath,ts);}const system=createSystem(fsMap);this._env=createVirtualTypeScriptEnvironment(system,Array.from(fsMap.keys()),ts,COMPILER_OPTIONS);return this}updateProgram(newFiles=[],removedFiles=[]){const errors=[];newFiles.forEach(([filePath,fileContent])=>{if(this._env.getSourceFile(filePath)){this._env.updateFile(filePath,fileContent);}else {this._env.createFile(filePath,fileContent);}});removedFiles.forEach(filePath=>{this._env.deleteFile(filePath);});this.parser=new ScriptParser(this._env);this.program=this._env.languageService.getProgram();this.parser.validateProgram(errors);this._errors=errors;return errors}getAllEsmScripts(fileName){const typeChecker=this.program.getTypeChecker();const pcTypes=this.program.getSourceFile("/playcanvas.d.ts");if(!pcTypes){throw new Error("PlayCanvas Types must be supplied")}const esmScripts=new Map;const sourceFile=this.program.getSourceFile(fileName);if(sourceFile===undefined){return esmScripts}if(sourceFile===null){throw new Error(`Source file ${fileName} not found`)}const nodes=getExportedNodes(this.program,sourceFile);const esmScriptClass=pcTypes.statements.find(node=>node.kind===typescriptExports.SyntaxKind.ClassDeclaration&&node.name.text==="Script")?.symbol;const isScriptNameMember=member=>typescriptExports.isPropertyDeclaration(member)&&typescriptExports.isIdentifier(member.name)&&member.name.text==="scriptName"&&isStaticMember(member)&&typescriptExports.isStringLiteral(member.initializer);nodes.forEach((node,name)=>{if(isAliasedClassDeclaration(node,typeChecker)&&inheritsFrom(node,typeChecker,esmScriptClass)){const scriptNameMember=node.members.find(isScriptNameMember);let finalName;if(scriptNameMember){finalName=scriptNameMember.initializer.text;}else {finalName=toLowerCamelCase(name);
|
|
434
|
+
const toLowerCamelCase=str=>str[0].toLowerCase()+str.substring(1);const SUPPORTED_ATTRIBUTE_TYPES=new Set(["Curve","Vec2","Vec3","Vec4","Color","number","string","boolean"]);const COMPILER_OPTIONS={noLib:true,strict:false,skipLibCheck:true,target:typescriptExports.ScriptTarget.ES2023,module:typescriptExports.ModuleKind.CommonJS,checkJs:true,allowJs:true,baseUrl:"./",paths:{"playcanvas":["/playcanvas.js"]}};class JSDocParser{async init(libPath){if(this._env){return this}let fsMap;if(!libPath){fsMap=await createDefaultMapFromNodeModules();}else if(libPath.endsWith(".d.ts")){const types=await fetch(libPath).then(r=>r.text());fsMap=new Map([["/lib.d.ts",types]]);}else {fsMap=await createDefaultMapFromCDN(COMPILER_OPTIONS,libPath,ts);}const system=createSystem(fsMap);this._env=createVirtualTypeScriptEnvironment(system,Array.from(fsMap.keys()),ts,COMPILER_OPTIONS);return this}updateProgram(newFiles=[],removedFiles=[]){const errors=[];newFiles.forEach(([filePath,fileContent])=>{if(this._env.getSourceFile(filePath)){this._env.updateFile(filePath,fileContent);}else {this._env.createFile(filePath,fileContent);}});removedFiles.forEach(filePath=>{this._env.deleteFile(filePath);});this.parser=new ScriptParser(this._env);this.program=this._env.languageService.getProgram();this.parser.validateProgram(errors);this._errors=errors;return errors}getAllEsmScripts(fileName,errors=[]){const typeChecker=this.program.getTypeChecker();const pcTypes=this.program.getSourceFile("/playcanvas.d.ts");if(!pcTypes){throw new Error("PlayCanvas Types must be supplied")}const esmScripts=new Map;const sourceFile=this.program.getSourceFile(fileName);if(sourceFile===undefined){return esmScripts}if(sourceFile===null){throw new Error(`Source file ${fileName} not found`)}const nodes=getExportedNodes(this.program,sourceFile);const esmScriptClass=pcTypes.statements.find(node=>node.kind===typescriptExports.SyntaxKind.ClassDeclaration&&node.name.text==="Script")?.symbol;const isScriptNameMember=member=>typescriptExports.isPropertyDeclaration(member)&&typescriptExports.isIdentifier(member.name)&&member.name.text==="scriptName"&&isStaticMember(member)&&typescriptExports.isStringLiteral(member.initializer);nodes.forEach((node,name)=>{if(isAliasedClassDeclaration(node,typeChecker)&&inheritsFrom(node,typeChecker,esmScriptClass)){const scriptNameMember=node.members.find(isScriptNameMember);let finalName;if(scriptNameMember){finalName=scriptNameMember.initializer.text;}else {finalName=toLowerCamelCase(name);const insertOffset=node.members.pos;const insertPos=node.getSourceFile().getLineAndCharacterOfPosition(insertOffset);errors.push(new ParsingError(node,"Missing Script Name","Scripts should have a static scriptName member that identifies the script.",{title:"Add scriptName",text:`
|
|
435
|
+
static scriptName = '${finalName}';
|
|
436
|
+
`,range:{startLineNumber:insertPos.line+1,startColumn:insertPos.character+1,endLineNumber:insertPos.line+1,endColumn:insertPos.character+1}}));}esmScripts.set(finalName,node);}});return esmScripts}parseAttributes(fileName){const results={};const errors=[...this._errors];if(errors.length>0){return [results,errors]}const nodes=this.getAllEsmScripts(fileName,errors);nodes.forEach((node,name)=>{const opts=results[name]={attributes:{},errors:[]};this.parser.extractAttributes(node,opts);});return [results,errors]}getAttributes(fileName){const results={};const errors=[...this._errors];if(errors.length>0){return [results,errors]}const typeChecker=this.program.getTypeChecker();const esmScripts=this.getAllEsmScripts(fileName,errors);const[attributes]=this.parseAttributes(fileName);const scripts=[];for(const[scriptName,node]of esmScripts){const members=[];for(const member of node.members){if(member.kind===typescriptExports.SyntaxKind.PropertyDeclaration&&!isStaticMember(member)){members.push(member);}}scripts.push({scriptName,members});errors.push(...attributes[scriptName]?.errors??[]);}for(const{scriptName,members}of scripts){const localMembers=[];for(const member of members){const name=member.name.getText();const attribute=attributes[scriptName].attributes[name];const isAttribute=!!attribute;const type=getType(member,typeChecker);if(!SUPPORTED_ATTRIBUTE_TYPES.has(type.name)){continue}const namePos=typescriptExports.getLineAndCharacterOfPosition(member.getSourceFile(),member.name.getStart());const jsdocNode=member.jsDoc&&member.jsDoc[member.jsDoc.length-1];const jsdocPos=jsdocNode?typescriptExports.getLineAndCharacterOfPosition(member.getSourceFile(),jsdocNode.getStart()):null;localMembers.push({name,isAttribute,type:type.name+(type.array?"[]":""),start:jsdocNode?{line:jsdocPos.line+1,column:jsdocPos.character+1}:{line:namePos.line+1,column:namePos.character+1},end:{line:namePos.line+1,column:namePos.character+name.length+1}});}results[scriptName]=localMembers;}return [results,errors]}constructor(){this._errors=[];}}
|
|
435
437
|
|
|
436
438
|
export { JSDocParser };
|
package/package.json
CHANGED
package/types/index.d.ts
CHANGED
|
@@ -34,9 +34,10 @@ export class JSDocParser {
|
|
|
34
34
|
/**
|
|
35
35
|
* Returns all the valid ESM Scripts within a file
|
|
36
36
|
* @param {string} fileName - The file name in the program to check
|
|
37
|
+
* @param {ParsingError[]} errors - An array to store any parsing errors
|
|
37
38
|
* @returns {Map<string, import('typescript').Node>} - A map of valid ESM Script <names, nodes> within the file
|
|
38
39
|
*/
|
|
39
|
-
getAllEsmScripts(fileName: string): Map<string, import("typescript").Node>;
|
|
40
|
+
getAllEsmScripts(fileName: string, errors?: ParsingError[]): Map<string, import("typescript").Node>;
|
|
40
41
|
/**
|
|
41
42
|
* Analyzes the specified TypeScript file and extracts metadata from JSDoc comments.
|
|
42
43
|
*
|
|
@@ -53,3 +54,4 @@ export class JSDocParser {
|
|
|
53
54
|
}
|
|
54
55
|
import { ScriptParser } from './parsers/script-parser.js';
|
|
55
56
|
import * as ts from 'typescript';
|
|
57
|
+
import { ParsingError } from './parsers/parsing-error.js';
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} TagDefinition
|
|
3
|
+
* @property {string} type - The type of the tag
|
|
4
|
+
* @property {() => string} supportMessage - A function to generate a support message for the tag
|
|
5
|
+
* @property {string} fix - A function to fix the tag
|
|
6
|
+
*/
|
|
1
7
|
/**
|
|
2
8
|
* A class to parse JSDoc comments and extract attribute metadata.
|
|
3
9
|
*/
|
|
@@ -5,13 +11,13 @@ export class AttributeParser {
|
|
|
5
11
|
/**
|
|
6
12
|
* Creates a new instance of the ScriptParser class.
|
|
7
13
|
*
|
|
8
|
-
* @param {Map<string,
|
|
14
|
+
* @param {Map<string, TagDefinition>} tags - An set of tag definitions and their validators to add to the parser
|
|
9
15
|
* @param {object} env - The TypeScript environment to use
|
|
10
16
|
* @param {Map<string, Function>} typeSerializerMap - A map of custom serializers
|
|
11
17
|
*/
|
|
12
|
-
constructor(tags: Map<string,
|
|
18
|
+
constructor(tags: Map<string, TagDefinition>, env: object, typeSerializerMap: Map<string, Function>);
|
|
13
19
|
env: any;
|
|
14
|
-
tagTypeAnnotations: Map<string,
|
|
20
|
+
tagTypeAnnotations: Map<string, TagDefinition>;
|
|
15
21
|
typeSerializerMap: Map<string, Function>;
|
|
16
22
|
program: any;
|
|
17
23
|
typeChecker: any;
|
|
@@ -51,5 +57,19 @@ export class AttributeParser {
|
|
|
51
57
|
*/
|
|
52
58
|
getMembers(node: ts.Node): any[];
|
|
53
59
|
}
|
|
60
|
+
export type TagDefinition = {
|
|
61
|
+
/**
|
|
62
|
+
* - The type of the tag
|
|
63
|
+
*/
|
|
64
|
+
type: string;
|
|
65
|
+
/**
|
|
66
|
+
* - A function to generate a support message for the tag
|
|
67
|
+
*/
|
|
68
|
+
supportMessage: () => string;
|
|
69
|
+
/**
|
|
70
|
+
* - A function to fix the tag
|
|
71
|
+
*/
|
|
72
|
+
fix: string;
|
|
73
|
+
};
|
|
54
74
|
import * as ts from 'typescript';
|
|
55
75
|
import { ParsingError } from './parsing-error.js';
|
|
@@ -1,6 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} Fix
|
|
3
|
+
* @property {string} text - The text to insert at the fix location
|
|
4
|
+
* @property {string} title - The title of the fix
|
|
5
|
+
* @property {{ startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number }} range - The range of the fix
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* A class representing a parsing error.
|
|
9
|
+
* @param {import('typescript').Node} node - The AST node which caused the error
|
|
10
|
+
* @param {string} type - The type of the error
|
|
11
|
+
* @param {string} message - The description of the error
|
|
12
|
+
* @param {Fix} [fix] - The fix for the error
|
|
13
|
+
*/
|
|
1
14
|
export class ParsingError {
|
|
2
|
-
constructor(node: any, message: any);
|
|
15
|
+
constructor(node: any, type: any, message: any, fix: any);
|
|
3
16
|
node: any;
|
|
17
|
+
type: any;
|
|
4
18
|
message: any;
|
|
19
|
+
fix: any;
|
|
5
20
|
toString(): string;
|
|
6
21
|
}
|
|
22
|
+
export type Fix = {
|
|
23
|
+
/**
|
|
24
|
+
* - The text to insert at the fix location
|
|
25
|
+
*/
|
|
26
|
+
text: string;
|
|
27
|
+
/**
|
|
28
|
+
* - The title of the fix
|
|
29
|
+
*/
|
|
30
|
+
title: string;
|
|
31
|
+
/**
|
|
32
|
+
* - The range of the fix
|
|
33
|
+
*/
|
|
34
|
+
range: {
|
|
35
|
+
startLineNumber: number;
|
|
36
|
+
startColumn: number;
|
|
37
|
+
endLineNumber: number;
|
|
38
|
+
endColumn: number;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
export function
|
|
2
|
-
export function getTag(tag: string, node: import("typescript").Node): import("typescript").Node;
|
|
1
|
+
export function getTag(tag: string, node: import("typescript").Node): import("typescript").JSDocTag | null;
|
|
3
2
|
export function hasTag(tag: string, node: import("typescript").Node): boolean;
|
|
4
3
|
export function isInterface(node: import("typescript").Node): boolean;
|
|
5
4
|
export function isStaticMember(member: import("typescript").Node): boolean;
|