@playcanvas/attribute-parser 1.7.5 → 1.8.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 +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -431,6 +431,6 @@ function zipArrays(a,b){const length=Math.max(a.length,b.length);const result=ne
|
|
|
431
431
|
|
|
432
432
|
const createNewExpressionParser=(name,argProcessor,defaultArr)=>{if(defaultArr===void 0)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)=>{if(defaultArr===void 0)defaultArr=[];return createNewExpressionParser(name,parseNumberArguments,defaultArr)};const createArrayArgumentParser=(name,defaultArr)=>{if(defaultArr===void 0)defaultArr=[];return 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){if(errors===void 0)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){if(opts===void 0)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 error=new ParsingError(member,`The attribute "${memberName}" is not initialized and does not have a @type tag.`);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){const error=new ParsingError(member,`Attribute "${memberName}" is an invalid type.`);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){if(newFiles===void 0)newFiles=[];if(removedFiles===void 0)removedFiles=[];const errors=[];newFiles.forEach(param=>{let[filePath,fileContent]=param;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);if(scriptNameMember){
|
|
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){if(newFiles===void 0)newFiles=[];if(removedFiles===void 0)removedFiles=[];const errors=[];newFiles.forEach(param=>{let[filePath,fileContent]=param;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);}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);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 pcTypes=this.program.getSourceFile("/playcanvas.d.ts");const esmScriptClass=pcTypes.statements.find(node=>node.kind===typescriptExports.SyntaxKind.ClassDeclaration&&node.name.text==="Script")?.symbol;const sourceFile=this.program.getSourceFile(fileName);if(!sourceFile){throw new Error(`Source file ${fileName} not found`)}const nodes=flatMapAnyNodes(sourceFile,node=>{if(!typescriptExports.isClassDeclaration(node)){return false}return inheritsFrom(node,typeChecker,esmScriptClass)||isInterface(node)});for(const node of nodes){const name=toLowerCamelCase(node.name.text);const members=[];for(const member of node.members){if(member.kind!==typescriptExports.SyntaxKind.PropertyDeclaration||isStaticMember(member)){continue}const tags=member.jsDoc?member.jsDoc.map(jsdoc=>jsdoc.tags?.map(tag=>tag.tagName.getText())??[]).flat():[];const name=member.name.getText();const type=getType(member,typeChecker);if(!type){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;const data={name,type:type.name+(type.array?"[]":""),isAttribute:tags.includes("attribute"),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}};members.push(data);}results[name]=members;}return [results,errors]}constructor(){this._errors=[];}}
|
|
435
435
|
|
|
436
436
|
export { JSDocParser };
|
package/package.json
CHANGED