npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

xacro-parser

v0.3.9

Published

Utility for parsing and converting ROS Xacro files in Javascript.

Downloads

2,631

Readme

xacro-parser

npm version build lgtm code quality github twitter sponsors

Javascript parser and loader for processing the ROS Xacro file format.

NOTE This package uses expr-eval for expression parsing.

Use

Loading Files From Disk in Node

import fs from 'fs';
import { XacroParser } from 'xacro-parser';
import { JSDOM } from 'jsdom';

// XacroParser depends on the browser xml parser.
global.DOMParser = new JSDOM().window.DOMParser;

const parser = new XacroParser();
parser.workingPath = './path/to/directory/';
parser.getFileContents = path => {

  return fs.readFile( path, { encoding: 'utf8' } );

};

const xacroContents = fs.readFileSync( './path/to/directory/file.xacro', { encoding: 'utf8' } );
parser.parse( xacroContents ).then( result => {

  // xacro XML

} );

Loading Files from Server


import fs from 'fs';
import { XacroParser } from 'xacro-parser';

fetch( './path/to/directory/file.xacro' )
  .then( res => res.text() )
  .then( xacroContents => {

    const parser = new XacroParser();
    parser.workingPath = './path/to/directory/';
    parser.getFileContents = path => {

      return fetch( path ).then( res => res.text() );

    };
    parser.parse( xacroContents ).then( result => {

      // xacro XML

    } );

} );

Using the Loader

import { XacroLoader } from 'xacro-parser';

// The working path is extracted automatically.
// Only works in the browser.
const loader = new XacroLoader();
loader.load(
    '../path/to/file.xacro',
    result => {

        // xacro XML

    },
    err => {

        // parse error

    } );

Different Versions of ROS

Xacro files from different versions of ROS require different options to be to be set. The differences are documented in the spec.

<= ROS Indigo

Options required for xacros created with a ROS version <= release 8.

parser.inOrder = false;
parser.requirePrefix = false;
parser.localProperties = false;

>= ROS Jade

Options required for xacros created with a ROS version >= release 9.

parser.inOrder = true;
parser.requirePrefix = true;
parser.localProperties = true;

API

XacroParser

.localProperties

localProperties = true : boolean

Since ROS Jade xacro scopes property definitions to the containing macro. Setting localProperties to false disables this behavior.

.requirePrefix

requirePrefix = true : boolean

Since ROS Jade xacro requires all tags be prefixed with "xacro:". Setting requirePrefix to false disables this requirement.

.inOrder

inOrder = true : boolean

Since ROS Jade xacro allows for in 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.

.workingPath

workingPath = '' : string

The working directory to search for dependent files in when parsing include tags. The path is required to end with '/'.

.arguments

arguments = {} : Object

A map of argument names to values that will be substituted for $(arg name) tags. These take precedence over any <xacro:arg> defaults.

loader.arguments =
  {
    transmission_hw_interface: "hardware_interface/PositionJointInterface",
    arm_x_separation: -0.4,
    laser_visual: true,
  };

.rospackCommands

rospackCommands = {} : ( ( command : String, ...args : Array<String> ) => String ) | Object

A map of rospack command stem to handling function that take all arguments as function parameters. An example implementation of the rospack find command:

loader.rospackCommands =
  {

    find: function( pkg ) {

      switch( pkg ) {

        case 'valkyrie_description':
          return '/absolute/path/to/valkyrie_description/';
        case 'r2_description':
          return '/absolute/path/to/r2_description/'

      }

    }

  };

Alternatively a function can be provided to evaluate the command:

load.rospackCommands = ( command, ...args ) => {

    if ( command === 'find' ) {

        const [ pkg ] = args;
        switch( pkg ) {
            case 'valkyrie_description':
                return '/absolute/path/to/valkyrie_description/';
            case 'r2_description':
                return '/absolute/path/to/r2_description/'
        }

    }

};

.parse

parse( contents : string ) : Promise<XMLDocument>

Parses the passed xacro contents using the options specified on the object and returns an xml document of the processed xacro file.

.getFileContents

getFileContents( path : string ) : Promise<string>

And 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.

XacroLoader

extends XacroParser

Extends XacroParse and implements getFileContents to load from a server using fetch.

.fetchOptions

fetchOptions = {} : Object

.load

load(
    url : string,
    onComplete : ( result : XMLDocument ) => void,
    onError? : ( error : Error ) => void
) : void

.parse

parse(
    url : string,
    onComplete : ( result : XMLDocument ) => void,
    onError? : ( error : Error ) => void
) : void

.parse

Limitations and Missing Features

Unimplemented Features

  • Macro argument pass-through using param:=^|default is not supported #5.
  • Calling macros with a dynamic name using the <xacro:call macro="${var}"/> syntax is not supported #9.
  • Include tag namespaces are not supported #12.

Limitations

  • 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.

Undocumented Xacro Behavior

While 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.

Default Parameter Value Assignment

The xacro documentation on default parameters only mentions the param:=default syntax. However, examples in the wild such as turtlebot_description appear to use param=default. This parser supports both syntaxes.

Macro Property Scope

The 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.

Include Block Macro Parameters Look at Incremental Children

The docs for the <xacro:macro params="*a *b" ... > 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.

Macro Call Contents are Evaluated Before Running a Macro

Consider the following:

<xacro:macro name="test" params="*a *b">
  <xacro:insert_block name="a"/>
  <xacro:insert_block name="b"/>
</xacro:macro>
<test>
  <xacro:if value="true">
    <child1/>
  </xacro:if>
  <xacro:if value="true">
    <child2/>
  </xacro:if>
  <child3/>
</test>

The 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.

Properties are Evaluated Immediately if "Local"

When 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.