{"id":15386994,"url":"https://github.com/gkjohnson/xacro-parser","last_synced_at":"2025-11-13T22:52:48.374Z","repository":{"id":38363366,"uuid":"193265569","full_name":"gkjohnson/xacro-parser","owner":"gkjohnson","description":"Utility for parsing and converting ROS Xacro files in Javascript.","archived":false,"fork":false,"pushed_at":"2025-06-21T23:17:06.000Z","size":2231,"stargazers_count":29,"open_issues_count":12,"forks_count":11,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-10-04T21:23:49.430Z","etag":null,"topics":["javascript","macro","parser","robot-operating-system","robotics","ros","urdf","urdf-models","xacro","xml"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gkjohnson.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"gkjohnson","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2019-06-22T18:14:42.000Z","updated_at":"2025-08-24T06:27:37.000Z","dependencies_parsed_at":"2024-02-28T09:42:17.327Z","dependency_job_id":"803d835a-98b0-49e9-8452-1e18c271476d","html_url":"https://github.com/gkjohnson/xacro-parser","commit_stats":{"total_commits":264,"total_committers":9,"mean_commits":"29.333333333333332","dds":"0.15909090909090906","last_synced_commit":"c3900bd5b703012c6441682569b0217aea9e601e"},"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/gkjohnson/xacro-parser","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gkjohnson%2Fxacro-parser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gkjohnson%2Fxacro-parser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gkjohnson%2Fxacro-parser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gkjohnson%2Fxacro-parser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gkjohnson","download_url":"https://codeload.github.com/gkjohnson/xacro-parser/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gkjohnson%2Fxacro-parser/sbom","scorecard":{"id":428953,"data":{"date":"2025-08-11","repo":{"name":"github.com/gkjohnson/xacro-parser","commit":"3b7ede837aeaadc50875c8a254b69e70a62639b9"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":3,"reason":"4 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 3","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'actions' permission set to 'read': .github/workflows/codeql.yml:16","Info: jobLevel 'contents' permission set to 'read': .github/workflows/codeql.yml:17","Warn: no topLevel permission defined: .github/workflows/codeql.yml:1","Warn: no topLevel permission defined: .github/workflows/node.js.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 1/13 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/gkjohnson/xacro-parser/codeql.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:30: update your workflow using https://app.stepsecurity.io/secureworkflow/gkjohnson/xacro-parser/codeql.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:36: update your workflow using https://app.stepsecurity.io/secureworkflow/gkjohnson/xacro-parser/codeql.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:39: update your workflow using https://app.stepsecurity.io/secureworkflow/gkjohnson/xacro-parser/codeql.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/node.js.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/gkjohnson/xacro-parser/node.js.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/node.js.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/gkjohnson/xacro-parser/node.js.yml/master?enable=pin","Warn: npmCommand not pinned by hash: .github/workflows/node.js.yml:28","Info:   0 out of   6 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":7,"reason":"SAST tool detected but not run on all commits","details":["Info: SAST configuration detected: CodeQL","Warn: 0 commits out of 18 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":1,"reason":"9 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-gcx4-mw62-g8wm","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-19T02:52:33.418Z","repository_id":38363366,"created_at":"2025-08-19T02:52:33.418Z","updated_at":"2025-08-19T02:52:33.418Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":284304462,"owners_count":26982161,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-11-13T02:00:06.582Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","macro","parser","robot-operating-system","robotics","ros","urdf","urdf-models","xacro","xml"],"created_at":"2024-10-01T14:51:07.476Z","updated_at":"2025-11-13T22:52:48.334Z","avatar_url":"https://github.com/gkjohnson.png","language":"JavaScript","funding_links":["https://github.com/sponsors/gkjohnson","https://github.com/sponsors/gkjohnson/"],"categories":[],"sub_categories":[],"readme":"# xacro-parser\n\n\n[![npm version](https://img.shields.io/npm/v/xacro-parser.svg?style=flat-square)](https://www.npmjs.com/package/xacro-parser)\n[![build](https://img.shields.io/github/actions/workflow/status/gkjohnson/xacro-parser/node.js.yml?style=flat-square\u0026label=build\u0026branch=master)](https://github.com/gkjohnson/xacro-parser/actions)\n[![github](https://flat.badgen.net/badge/icon/github?icon=github\u0026label)](https://github.com/gkjohnson/xacro-parser/)\n[![twitter](https://flat.badgen.net/badge/twitter/@garrettkjohnson/?icon\u0026label)](https://twitter.com/garrettkjohnson)\n[![sponsors](https://img.shields.io/github/sponsors/gkjohnson?style=flat-square\u0026color=1da1f2)](https://github.com/sponsors/gkjohnson/)\n\nJavascript parser and loader for processing the [ROS Xacro file format](http://wiki.ros.org/xacro).\n\n**NOTE**\n_This package uses [expr-eval](https://github.com/silentmatt/expr-eval) for expression parsing._\n\n# Use\n\n## Loading Files From Disk in Node\n\n```js\nimport fs from 'fs';\nimport { XacroParser } from 'xacro-parser';\nimport { JSDOM } from 'jsdom';\n\n// XacroParser depends on the browser xml parser.\nglobal.DOMParser = new JSDOM().window.DOMParser;\n\nconst parser = new XacroParser();\nparser.workingPath = './path/to/directory/';\nparser.getFileContents = path =\u003e {\n\n  return fs.readFile( path, { encoding: 'utf8' } );\n\n};\n\nconst xacroContents = fs.readFileSync( './path/to/directory/file.xacro', { encoding: 'utf8' } );\nparser.parse( xacroContents ).then( result =\u003e {\n\n  // xacro XML\n\n} );\n```\n\n## Loading Files from Server\n\n```js\n\nimport { XacroParser } from 'xacro-parser';\n\nfetch( './path/to/directory/file.xacro' )\n  .then( res =\u003e res.text() )\n  .then( xacroContents =\u003e {\n\n    const parser = new XacroParser();\n    parser.workingPath = './path/to/directory/';\n    parser.getFileContents = path =\u003e {\n\n      return fetch( path ).then( res =\u003e res.text() );\n\n    };\n    parser.parse( xacroContents ).then( result =\u003e {\n\n      // xacro XML\n\n    } );\n\n} );\n\n```\n\n## Using the Loader\n\n```js\nimport { XacroLoader } from 'xacro-parser';\n\n// The working path is extracted automatically.\n// Only works in the browser.\nconst loader = new XacroLoader();\nloader.load(\n    '../path/to/file.xacro',\n    result =\u003e {\n\n        // xacro XML\n\n    },\n    err =\u003e {\n\n        // parse error\n\n    } );\n```\n\n# Different Versions of ROS\n\nXacro files from different versions of ROS require different options to be to be set. The differences are documented in the [spec](http://wiki.ros.org/xacro).\n\n## \u003c= ROS Indigo\n\nOptions required for xacros created with a ROS version \u003c= release 8.\n\n```js\nparser.inOrder = false;\nparser.requirePrefix = false;\nparser.localProperties = false;\n```\n\n## \u003e= ROS Jade\n\nOptions required for xacros created with a ROS version \u003e= release 9.\n\n```js\nparser.inOrder = true;\nparser.requirePrefix = true;\nparser.localProperties = true;\n```\n\n# API\n\n## XacroParser\n\n### .localProperties\n\n```js\nlocalProperties = true : boolean\n```\n\nSince `ROS Jade` xacro [scopes property definitions to the containing macro](http://wiki.ros.org/xacro#Local_properties). Setting `localProperties` to false disables this behavior.\n\n### .requirePrefix\n\n```js\nrequirePrefix = true : boolean\n```\n\nSince `ROS Jade` xacro [requires all tags be prefixed with \"xacro:\"](http://wiki.ros.org/xacro#Deprecated_Syntax). Setting `requirePrefix` to false disables this requirement.\n\n### .inOrder\n\n```js\ninOrder = true : boolean\n```\n\nSince `ROS Jade` xacro allows for [in order](http://wiki.ros.org/xacro#Processing_Order) processing, which allows variables to be used to define include paths and order-dependent property definitions. The equivalent of the `--inorder` xacro command line flag.\n\n### .workingPath\n\n```js\nworkingPath = '' : string\n```\n\nThe working directory to search for dependent files in when parsing `include` tags. The path is required to end with '/'.\n\n### .arguments\n\n```js\narguments = {} : Object\n```\n\nA map of argument names to values that will be substituted for `$(arg name)` tags. These take precedence over any `\u003cxacro:arg\u003e` defaults.\n\n```js\nloader.arguments =\n  {\n    transmission_hw_interface: \"hardware_interface/PositionJointInterface\",\n    arm_x_separation: -0.4,\n    laser_visual: true,\n  };\n```\n\n### .rospackCommands\n\n```js\nrospackCommands = {} : ( ( command : String, ...args : Array\u003cString\u003e ) =\u003e String ) | Object\n```\n\nA map of rospack command stem to handling function that take all arguments as function parameters. An example implementation of the `rospack find` command:\n\n```js\nloader.rospackCommands =\n  {\n\n    find: function( pkg ) {\n\n      switch( pkg ) {\n\n        case 'valkyrie_description':\n          return '/absolute/path/to/valkyrie_description/';\n        case 'r2_description':\n          return '/absolute/path/to/r2_description/'\n\n      }\n\n    }\n\n  };\n```\n\nAlternatively a function can be provided to evaluate the command:\n\n```js\nload.rospackCommands = ( command, ...args ) =\u003e {\n\n    if ( command === 'find' ) {\n\n        const [ pkg ] = args;\n        switch( pkg ) {\n            case 'valkyrie_description':\n                return '/absolute/path/to/valkyrie_description/';\n            case 'r2_description':\n                return '/absolute/path/to/r2_description/'\n        }\n\n    }\n\n};\n```\n\n### .parse\n\n```js\nparse( contents : string ) : Promise\u003cXMLDocument\u003e\n```\n\nParses the passed xacro contents using the options specified on the object and returns an xml document of the processed xacro file.\n\n### .getFileContents\n\n```js\ngetFileContents( path : string ) : Promise\u003cstring\u003e\n```\n\nAnd overrideable function that takes a file path and returns the contents of that file as a string. Used for loading a documents referenced in `include` tags.\n\n## XacroLoader\n\n_extends [XacroParser](#XacroParser)_\n\nExtends XacroParse and implements `getFileContents` to load from a server using fetch.\n\n### .fetchOptions\n\n```js\nfetchOptions = {} : Object\n```\n\n### .load\n\n```js\nload(\n    url : string,\n    onComplete : ( result : XMLDocument ) =\u003e void,\n    onError? : ( error : Error ) =\u003e void\n) : void\n```\n\n### .parse\n\n```js\nparse(\n    url : string,\n    onComplete : ( result : XMLDocument ) =\u003e void,\n    onError? : ( error : Error ) =\u003e void\n) : void\n```\n\n### .parse\n\n# Limitations and Missing Features\n\n## Unimplemented Features\n\n- Macro argument pass-through using `param:=^|default` is not supported [#5](https://github.com/gkjohnson/xacro-parser/issues/5).\n- Calling macros with a dynamic name using the `\u003cxacro:call macro=\"${var}\"/\u003e` syntax is not supported [#9](https://github.com/gkjohnson/xacro-parser/issues/9).\n- Include tag namespaces are not supported [#12](https://github.com/gkjohnson/xacro-parser/issues/12).\n\n## Limitations\n\n- The official xacro parser supports using basically any Python syntax in the `${}` syntax which can't be easily supported in Javascript. Instead basic argument substitution and expression evaluation is supported using the `expr-eval` package which may not support all expression types. Please submit an issue if evaluation fails to work on a file.\n\n# Undocumented Xacro Behavior\n\nWhile the documentation for the xacro format is relatively complete there are some features that cannot necessarily be well understood without looking at code or tests.\n\n## Default Parameter Value Assignment\n\nThe xacro documentation on [default parameters](http://wiki.ros.org/xacro#Default_parameters) only mentions the `param:=default` syntax. However, examples in the wild such as [turtlebot_description](https://github.com/turtlebot/turtlebot/blob/melodic/turtlebot_description/urdf/stacks/hexagons.urdf.xacro#L13) appear to use `param=default`. This parser supports both syntaxes.\n\n## Macro Property Scope\n\nThe `xacro:property` tags can have a `scope` attribute on them that can take \"global\" and \"parent\" values, which adds the property to the global or parent scope respetively. Neither of these is the default, though. If the scope is not specified then the variable is only relevant to the macro scope.\n\n## Include Block Macro Parameters Look at Incremental Children\n\nThe docs for the `\u003cxacro:macro params=\"*a *b\" ... \u003e` syntax makes it look like it's important that the name of the `*` parameters be the same as the tag they are including or that they always reference the first element but this is not the case. Instead the first `*` parameter refers the first one and the second one refers to the second element and so on.\n\n## Macro Call Contents are Evaluated _Before_ Running a Macro\n\nConsider the following:\n\n```xml\n\u003cxacro:macro name=\"test\" params=\"*a *b\"\u003e\n  \u003cxacro:insert_block name=\"a\"/\u003e\n  \u003cxacro:insert_block name=\"b\"/\u003e\n\u003c/xacro:macro\u003e\n\u003ctest\u003e\n  \u003cxacro:if value=\"true\"\u003e\n    \u003cchild1/\u003e\n  \u003c/xacro:if\u003e\n  \u003cxacro:if value=\"true\"\u003e\n    \u003cchild2/\u003e\n  \u003c/xacro:if\u003e\n  \u003cchild3/\u003e\n\u003c/test\u003e\n```\n\nThe macro \"test\" includes the first and second elements of the caller when generating the contents. The contents of the caller element are to be evaluated first _before_ evaluating the macro, though, which means the if statements will be removed and test will be left with `child1` and `child3` before the elements are included in the test macro body.\n\n## Properties are Evaluated Immediately if \"Local\"\n\nWhen tracking properties the unevaluated expression itself is added to the property scope and evaluated when used in an attribute. _However_ when a property is scoped locally (as in does not have a global or parent scope property) then it is evaluated immediately, as seen [here](https://github.com/ros/xacro/blob/melodic-devel/src/xacro/__init__.py#L565).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgkjohnson%2Fxacro-parser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgkjohnson%2Fxacro-parser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgkjohnson%2Fxacro-parser/lists"}