@arcgis/core
Version:
ArcGIS Maps SDK for JavaScript: A complete 2D and 3D mapping and data visualization API
6 lines (5 loc) • 14.1 kB
JavaScript
/*
All material copyright ESRI, All Rights Reserved, unless otherwise specified.
See https://js.arcgis.com/4.32/esri/copyright.txt for details.
*/
import{i as e,$ as i,a as t,f as n,B as s,Y as a,V as o}from"../../chunks/index.js";import{DiagnosticCodes as r,diagnosticMessages as d}from"./diagnosticMessages.js";import{arcadeReservedKeywords as c}from"./languageKeywords.js";import{DiagnosticSeverity as l}from"./types.js";function h(e,i){return i?e.replaceAll(/\${(.*?)}/g,((e,t)=>i[t]?.toString()??"")):e}function f(e){return!!e&&"dictionary"!==e.type}class p{constructor(e,i=[]){this._apiDefinitions=e,this._profileVariables=i,this._isInBlock=!1,this._identifierBeingAssigned=void 0,this._assignmentValidationMode="disabled",this._scriptScopeIdentifiers=new Map,this._diagnostics=new Array,this._undeclaredIdentifiersInFunctions=new Map}validateScript(i){if(!i)return{diagnostics:[],program:null};this._isInBlock=!1,this._identifierBeingAssigned=void 0,this._assignmentValidationMode="disabled",this._diagnostics=[],this._scriptScopeIdentifiers.clear(),this._undeclaredIdentifiersInFunctions.clear(),this._functionScopeIdentifiers=void 0;let t=null;try{t=e(i,{tolerant:!0}),this.handleErrors(t.errors),t.body.forEach((e=>this.validateStatement(e))),this.diagnoseIdentifiers(),this._undeclaredIdentifiersInFunctions.size>0&&this._undeclaredIdentifiersInFunctions.forEach((e=>{for(const i of e)this.logDiagnostic(i.node.loc,{code:r.NotDefined,data:{identifier:i.node.name}})}))}catch(n){this.handleException(n)}return{diagnostics:this._diagnostics,program:t}}disableRecordIdentifierAssignment(e,i){const t=this._assignmentValidationMode;this._assignmentValidationMode="disabled",e.call(this,i),this._assignmentValidationMode=t}inBlock(e,i){const t=this._isInBlock;this._isInBlock=!0,e.call(this,i),this._isInBlock=t}get _isInFunctionScope(){return!!this._functionScopeIdentifiers}inFunctionScope(e){this._functionScopeIdentifiers=new Map,e.call(this),this.diagnoseIdentifiers(),this._functionScopeIdentifiers=void 0}logDiagnostic(e,t){const n={severity:l.Error,...t,message:h(d[t.code]??i[t.code],t.data),range:{start:{line:e.start.line-1,character:e.start.column},end:{line:e.end.line-1,character:e.end.column}}};this._diagnostics.push(n)}handleErrors(e){(e??[]).forEach(this.handleException,this)}handleException(e){if(g(e)){const{range:i,code:t,data:n}=e;this.logDiagnostic(i,{code:t,data:n})}else this.logDiagnostic({start:{line:1,column:0},end:{line:1,column:0}},{code:r.ExecutionError,data:{stack:e.stack}})}getIdentifierInfo(e){return this._functionScopeIdentifiers?.get(e)??this._scriptScopeIdentifiers.get(e)}setIdentiferInfo(e,i){this._functionScopeIdentifiers?this._functionScopeIdentifiers.set(e,i):((this._functionScopeIdentifiers??this._scriptScopeIdentifiers).set(e,i),this._functionScopeIdentifiers||this._undeclaredIdentifiersInFunctions.has(e)&&(this._undeclaredIdentifiersInFunctions.delete(e),i.used=!0))}isProfileVariable(e){return(this._profileVariables??[]).some((i=>i.name.toLowerCase()===e))}isApiItem(e){const i=!!this._apiDefinitions?.constantDefinitions?.get(e),t=!!this._apiDefinitions?.functionDefinitions?.get(e);return i||t}validateStatement(e){if(e)switch(e.type){case t.BlockStatement:return void e.body.forEach((e=>this.validateStatement(e)));case t.VariableDeclaration:return void e.declarations.forEach((e=>this.validateVariableDeclarator(e)));case t.FunctionDeclaration:return void this.validateFunctionDeclaration(e);case t.ExportNamedDeclaration:return void this.validateExportDeclaration(e);case t.ImportDeclaration:return void this.validateImportDeclaration(e);case t.WhileStatement:return void this.validateWhileStatement(e);case t.ForStatement:return void this.validateForStatement(e);case t.ForInStatement:return void this.validateForInStatement(e);case t.IfStatement:return void this.validateIfStatement(e);case t.ReturnStatement:return void this.validateExpression(e.argument);case t.ExpressionStatement:return void this.validateExpression(e.expression);case t.BreakStatement:case t.ContinueStatement:case t.EmptyStatement:return}}validateVariableDeclarator(e){this.validateExpression(e.init),this.recordVariableIdentifier(e.id,{initialized:!!e.init})}validateFunctionDeclaration(e){this.recordFunctionIdentifier(e),this.inFunctionScope((()=>{e.params.forEach((e=>this.recordParamAsIdentifier(e))),m(e.body)&&this.logDiagnostic(e.body.loc,{code:r.UnexpectedEmptyFunction,data:{identifier:e.id.name},severity:l.Warning}),this.validateStatement(e.body)}))}validateExportDeclaration(e){this.validateStatement(e.declaration)}validateImportDeclaration(e){this.recordImportIdentifier(e)}validateForStatement(e){n(e.init)?this.inBlock(this.validateStatement,e.init):this.validateExpression(e.init),this.validateExpression(e.update),this.validateExpression(e.test),m(e.body)&&this.logDiagnostic(e.body.loc,{code:r.EmptyBlockStatement,severity:l.Warning}),this.inBlock(this.validateStatement,e.body)}validateWhileStatement(e){this.validateExpression(e.test),m(e.body)&&this.logDiagnostic(e.body.loc,{code:r.EmptyBlockStatement,severity:l.Warning}),this.inBlock(this.validateStatement,e.body)}validateForInStatement(e){s(e.left)?this.validateExpression(e.left):this.recordVariableIdentifier(e.left.declarations[0].id,{initialized:!0,inBlock:!0}),this.validateExpression(e.right),m(e.body)&&this.logDiagnostic(e.body.loc,{code:r.EmptyBlockStatement,severity:l.Warning}),this.validateStatement(e.body)}validateIfStatement(e){this.validateExpression(e.test),m(e.consequent)&&this.logDiagnostic(e.consequent.loc,{code:r.EmptyBlockStatement,severity:l.Warning}),e.alternate&&m(e.alternate)&&this.logDiagnostic(e.alternate.loc,{code:r.EmptyBlockStatement,severity:l.Warning}),this.inBlock(this.validateStatement,e.consequent),this.inBlock(this.validateStatement,e.alternate)}validateExpression(e){if(e)switch(e.type){case t.AssignmentExpression:return void this.validateAssignmentExpression(e);case t.CallExpression:return void this.validateCallExpression(e);case t.Identifier:return void this.validateIdentifier(e);case t.Literal:return void this.validateLiteral(e);case t.ArrayExpression:return void e.elements.forEach((e=>this.validateExpression(e)));case t.ObjectExpression:return void this.validateObjectExpression(e);case t.UnaryExpression:return void this.validateUnaryExpression(e);case t.UpdateExpression:return void this.validateUpdateExpression(e);case t.BinaryExpression:case t.LogicalExpression:return void this.validateBinaryAndLogicalExpression(e);case t.MemberExpression:return void this.validateMemberExpression(e);case t.TemplateLiteral:return void e.expressions.forEach((e=>this.validateExpression(e)));default:return}}validateAssignmentExpression(e){const i=this._identifierBeingAssigned,t=this._assignmentValidationMode;s(e.left)&&(this._identifierBeingAssigned=e.left.name.toLowerCase(),this._assignmentValidationMode="left"),this.validateExpression(e.left),s(e.left)&&(this._assignmentValidationMode="right"),this.validateExpression(e.right),this._identifierBeingAssigned=i,this._assignmentValidationMode=t}validateCallExpression(e){if(this.validateExpression(e.callee),s(e.callee)){const i=e.callee.name.toLowerCase(),t=this.getIdentifierInfo(i),n=this._apiDefinitions?.functionDefinitions?.get(i);if(!t&&n){const i=u(n,e.arguments.length);i&&this.logDiagnostic(e.loc,i)}}e.arguments.forEach((e=>this.validateExpression(e)))}validateIdentifier(e){const i=e.name.toLowerCase(),t=this.getIdentifierInfo(i);if(t){if("left"===this._assignmentValidationMode&&this._identifierBeingAssigned===i)return void(t.initialized=!0);if("right"===this._assignmentValidationMode&&this._identifierBeingAssigned===i)return;t.used=!0}else if(!this.isProfileVariable(i)&&!this.isApiItem(i)){if(this._isInFunctionScope){let t=this._undeclaredIdentifiersInFunctions.get(i);return t||(t=[],this._undeclaredIdentifiersInFunctions.set(i,t)),void t.push({node:e,identifier:i})}this.logDiagnostic(e.loc,{code:r.NotDefined,data:{identifier:e.name}})}}validateLiteral(e){I(e.raw).forEach((e=>{const i=this.getIdentifierInfo(e.toLowerCase());i&&(i.used=!0)}))}logProfileOrApiConflict(e){const i=e.name.toLowerCase(),t=this.isProfileVariable(i),n=this.isApiItem(i);(t||n)&&this.logDiagnostic(e.loc,{code:t?r.ProfileVariablesConflict:r.ApiConflict,severity:l.Warning,data:{identifier:e.name}})}logReservedKeywordsConflict(e){const i=e.name.toLowerCase();c.includes(i)&&this.logDiagnostic(e.loc,{code:r.ReservedKeyword,severity:l.Warning,data:{identifier:e.name}})}validateObjectExpression(e){e.properties.forEach((e=>{this.validateExpression(e.value)}))}validateUnaryExpression(e){this.validateExpression(e.argument)}validateUpdateExpression(e){this.validateExpression(e.argument)}validateBinaryAndLogicalExpression(e){this.validateExpression(e.left),this.validateExpression(e.right)}validateMemberExpression(e){const i=this.flattenMemberExpressionAndValidate(e),t=i[0].object;this.disableRecordIdentifierAssignment(this.validateExpression,t),s(t)&&(this.getIdentifierInfo(t.name.toLowerCase())||this.validateMemberExpressionWithProfile(i)||this.validateConstantMemberExpression(i))}flattenMemberExpressionAndValidate(e){return e.type===t.MemberExpression?(s(e.property)&&!e.computed||this.validateExpression(e.property),[...this.flattenMemberExpressionAndValidate(e.object),e]):[]}extractAndValidatePropertyName(e){switch(e.type){case"Identifier":return e.name.toLowerCase();case"Literal":return"string"!=typeof e.value?(this.logDiagnostic(e.loc,{code:r.UnexpectedPropertyIdentifier}),null):e.value.toLowerCase();default:return this.logDiagnostic(e.loc,{code:r.UnexpectedPropertyIdentifier}),null}}validateConstantMemberExpression(e){const i=e[0];if(!s(i.object))return!1;const t=i.object.name.toLowerCase(),n=this._apiDefinitions?.constantDefinitions?.get(t);if(!n)return!1;if("namespace"!==n.type)return this.logDiagnostic(i.property.loc,{code:r.NotADictionary,data:{identifier:t}}),!0;const a=this.extractAndValidatePropertyName(i.property);if(!a)return!0;if(!n.members.some((e=>e.key===a))){const e=n.members.reduce(((e,i)=>`${e}${e?" | ":""}${i.completion.label.split(".").pop()}`),"");this.logDiagnostic(i.property.loc,{code:r.InvalidConstantIdentifier,data:{list:e}})}return e.length>1&&this.logDiagnostic(e[1].property.loc,{code:r.UnexpectedPropertyIdentifier}),!0}validateMemberExpressionWithProfile(e){const i=e[0];if(i.object.type!==t.Identifier)return!1;const n=i.object.name.toLowerCase(),s=this._profileVariables?.find((e=>e.key===n));if(!s)return!1;if(f(s))return this.logDiagnostic(i.object.loc,{code:r.NotADictionary,data:{identifier:s.name}}),!0;if(this._identifierBeingAssigned===n)return this.logDiagnostic(i.loc,{code:r.ProfileVariablesAreImmutable}),!0;let a=s;for(let t=0;t<e.length;t++){if(e[t].computed)return!0;if(f(a))return this.logDiagnostic(e[t-1]?.property.loc??e[t].object.loc,{code:r.NotADictionary,data:{identifier:a.name}}),!0;const i=this.extractAndValidatePropertyName(e[t].property);if(!i)return!0;if(0===a.properties.length)return this.logDiagnostic(e[t].property.loc,{code:r.UnknownPropertyIdentifier,data:{identifier:i},severity:l.Warning}),!0;const n=a.properties.find((e=>e.name.toLowerCase()===i));if(!n){const i=a.properties.reduce(((e,i)=>`${e}${e?" | ":""}${i.name.split(".").pop()}`),"");return this.logDiagnostic(e[t].property.loc,{code:r.InvalidPropertyIdentifier,data:{list:i}}),!0}a=n}return!0}recordVariableIdentifier(e,i){this.logReservedKeywordsConflict(e),this.logProfileOrApiConflict(e);const t=e.name.toLowerCase();let n=this.getIdentifierInfo(t);const s=n&&this._isInFunctionScope&&"function"===n.scope,a=n&&!this._isInFunctionScope&&"script"===n.scope;(s||a)&&this.logDiagnostic(e.loc,{code:r.AlreadyDefined,data:{identifier:e.name},severity:l.Warning});const o=i.inBlock??this._isInBlock,{initialized:d}=i;if(!n||this._isInFunctionScope&&"function"!==n.scope){n={node:e,used:!1,initialized:d,scope:this._isInFunctionScope?"function":o?"block":"script"}}else n.node=e,n.used=!1,n.initialized=d;return"block"!==n.scope||o||(n.scope="script",this.logDiagnostic(e.loc,{code:r.AlreadyDefined,data:{identifier:e.name},severity:l.Warning})),this.setIdentiferInfo(t,n),!1}recordImportIdentifier(e){const i=e.specifiers[0].local;this.logProfileOrApiConflict(i);const t=i.name.toLowerCase();let n=this.getIdentifierInfo(t);n?.scope&&this.logDiagnostic(e.specifiers[0].local.loc,{code:r.AlreadyDefined,data:{identifier:e.specifiers[0].local.name},severity:l.Warning}),n={node:e.specifiers[0].local,used:!1,...n,scope:"script",initialized:!0},this.setIdentiferInfo(t,n)}recordFunctionIdentifier(e){this.logProfileOrApiConflict(e.id);const i=e.id.name.toLowerCase();let t=this.getIdentifierInfo(i);t?.scope&&this.logDiagnostic(e.id.loc,{code:r.AlreadyDefined,data:{identifier:e.id.name},severity:l.Warning}),t={node:e.id,used:!1,...t,scope:"script",initialized:!0},this.setIdentiferInfo(i,t)}recordParamAsIdentifier(e){return this.recordVariableIdentifier(e,{initialized:!0})}diagnoseIdentifiers(){(this._functionScopeIdentifiers??this._scriptScopeIdentifiers).forEach((e=>{e.node&&(e.used?e.initialized||this.logDiagnostic(e.node.loc,{code:r.DefinedNeverAssigned,data:{identifier:e.node.name},severity:l.Warning}):this.logDiagnostic(e.node.loc,{code:e.initialized?r.AssignedNeverUsed:r.DefinedNeverUsed,data:{identifier:e.node.name},severity:l.Warning}))}))}}function g(e){return"ParsingError"===e?.name}function m(e){return a(e)||o(e)&&!e?.body.length}function u(e,i){if(!e||null==i||i<0)return null;const{min:t,max:n}=e.overloads.reduce(((e,i)=>{const{min:t,max:n}=i.parametersInfo;return e.min>=0&&(e.min=Math.min(t,e.min)),e.max>=0&&(e.max=n<0?n:Math.max(n,e.max)),e}),{min:1/0,max:0});return i<t?t>0?{code:r.NotEnoughArguments,data:{min:t}}:{code:r.NoArgumentExpected}:n>=0&&i>n?{code:r.TooManyArguments,data:{max:n}}:null}const v=new RegExp(/\B@\w+/g);function I(e){return Array.from((e??"").matchAll(v),(e=>e[0].slice(1)))}function y(e,i=[],t){if(!e)return{diagnostics:[]};return new p(t,i).validateScript(e)}export{p as ArcadeValidationService,h as format,f as isValueVariable,y as validateScript};