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 🙏

© 2025 – Pkg Stats / Ryan Hefner

grunt-xmlstoke

v0.7.1

Published

CRUD for nodes in XML files based on XPath queries

Downloads

6,208

Readme

grunt-xmlstoke

An extended version of grunt-xmlpoke by Brian Dukes - a gruntjs port of the xmlpoke NAnt task.

In addition to updating the values of existing nodes, as provided by grunt-xmlpoke, grunt-xmlstoke can perform all basic CRUD operations on XML Files: creating/inserting new nodes, deleting existing nodes, and reading the values of existing nodes (to then save them in grunt.option).

The current API should be completely backward-compatible with grunt-xmlpoke

If you haven't used Grunt before, be sure to check out the Getting Started guide, as it explains how to create a Gruntfile as well as install and use Grunt plugins. Once you're familiar with that process, you may install this plugin with this command:

npm install grunt-xmlstoke --save-dev

Once the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript:

grunt.loadNpmTasks('grunt-xmlstoke');

Overview

In your project's Gruntfile, add a section named xmlstoke to the data object passed into grunt.initConfig().

grunt.initConfig({
  xmlstoke: {
    updateTitle: {
      options: {
        actions: [{
          xpath: '//title',
          value: 'The Good Parts'
        }],
      }
      files: { 'dest.xml': 'src.xml' },
    },
  },
})

First character should be:

C or I - Create/Insert
R - Read
U - Update (this is the default, type can be left blank for Update as well)
D - Delete

For Insertion operations, this points to the parentNode/ownerNode to have the new elements/attributes inserted into.

For Updates, this function is passed the selected node. For Insertions, this function is passed the selected parentNode/ownerNode.

The name of the node to be inserted, e.g. "my-node" to insert <my-node/> into, or "@myattr" to add the myattr attribute to the node(s) selected with action.xpath. Can also contain an XPath index (NOTE: unlike JavaScript Arrays ,XPath is 1-index) which is stripped from the name upon node creation, in order to create more than one of a kind:

actions: [
  { type: 'I', xpath: '/cds', node: 'cd', value: 'Slayer' },
  { type: 'I', xpath: '/cds', node: 'cd', value: 'More Slayer' }
]

Would find that /cds/cd already exists after the first insertion, and instead update its value (compare: mysql insert on duplicate key update).

actions: [
  { type: 'I', xpath: '/cds', node: 'cd[1]', value: 'Slayer' },
  { type: 'I', xpath: '/cds', node: 'cd[2]', value: 'More Slayer' }
]

Would correctly insert two different CDs, the index being optional in the first line.

Examples:


callback: function (readValues) { // readValues := "100"
    // Typecast to int before storing
    return parseInt(readValues, 10);
}

callback: function (readValues) { // readValues := ["A", "B"]
    if (readValues.length < 3) {
        // Return null to throw a grunt.error and abort the task
        // grunt.log.verbose("Aborting because there were only 2 nodes, expecting: 3+");
        return null;
    }
    return readValues;
}

callback: function (readValues) { // readValues := null
    if (readValues === null) {
        // Prevent grunt from throwing an Error because no nodes matched
        // grunt.log.verbose("Config elem not found, using default");
        return grunt.option('myDefaultValue');;
    }
    return readValues;
}

An array of Action objects, see Actions Options. Examples:

actions: [
  // Delete all <foo> nodes
  {type: 'D', xpath: '//foo'},
  
  // Insert <baz> into all <baz> under /myroot
  {type: 'I', xpath: '/rootelem/bar', node: 'baz', },
  
  // Add an athe foobar="100" attribute to them
  {type: 'I', xpath: '/rootelem/bar/baz', node: '@foobar', value: '100'},
  
  // Change it to 200 for the second of those <baz>, type=update assumed as default
  {xpath: '/rootelem/bar/baz[2]/@foobar', value: '200'},
  
  // Read the value of the foobar attr from the 5th overall occurence of <baz>,
  // Save it in grunt.option as "myData"
  {type: 'R', xpath: '//baz[5]/@foobar', saveAs: 'myData' }
}]

Alternatively, the reads, deletions, insertions and updates/replacements shorthands can be used instead of actions. That way you don't have to specify the action type manually. The config arrays are processed in the order reads, deletions, insertions, replacements || updates, actions.

options.updates (alias: options.replacements)

Type: Array, Default value: undefined An Array of Update Actions. action.type is set automatically.

options.deletions

Type: Array, Default value: undefined An Array of Deletion Actions. action.type is set automatically.

options.reads

Type: Array, Default value: undefined An Array of Read Actions. action.type is set automatically.

options.insertions

Type: Array, Default value: undefined An Array of Insertion Actions. action.type is set automatically.

Example - Update with function as value

In this example, the value of an attribute is modified. So if the test.xml file has the content <x y="abc" />, the generated result in this case would be <x y="ABC" />.

grunt.initConfig({
  xmlstoke: {
    upperCaseAttr: {
      options: {
        actions: [{
          xpath: '/x/@y',
          value: function (node) { return node.value.toUpperCase(); }
        }]
      },
      files: {
        'dest/output.xml': 'src/test.xml',
      },
    },
  },
})

Example - Multiple XPath Selectors

In this example, the same value is updated in multiple locations. So if the testing.xml file has the content <x y="999" />, the generated result in this case would be <x y="111">111</x>.

grunt.initConfig({
  xmlstoke: {
    updateAllTheThings: {
      options: {
        actions: [{
          xpath: ['/x/@y','/x'],
          value: '111'
        ]}
      },
      files: {
        'dest/output.xml': 'src/test.xml',
      },
    },
  },
})

Example - Deleting Nodes

Given <x><a /><a /><b /><c /></x>, will delete all matched nodes and return <x></x> (<x />)

grunt.initConfig({
  xmlstoke: {
    deleteStuff: {
      options: {
        actions: [{
          type: 'del'
          xpath: ['/x/a', '//b'],
        }]
      },
      files: {
        'dest/output.xml': 'src/test.xml',
      },
    },
  },
})

Example - Inserting Elements and Attributes

Given <x a="1"></x>, returns <x a="2"><foo bar="baz"/></a>. Notice how an Insertion just performs an update if the node exists already as seen with the @a attribute

grunt.initConfig({
  xmlstoke: {
    updateAllTheThings: {
      options: {
        actions: [{
          type: 'ins'
          xpath: '//x',
          node: '@a',
          value: '2'
        }, {
          type: 'ins'
          xpath: '//x',
          node: 'foo'
        }, {
          type: 'ins'
          xpath: '//x/foo',
          node: '@bar'
          value: 'baz'
        }]
      },
      files: {
        'dest/output.xml': 'src/test.xml',
      },
    },
  },
})

Example - Namespaces and Reads

This example covers both basic Reads and namespaces. options.namespaces must be specified whenever operations inolve namespaces. After that, simply use the usual ns:elemName or ns:attrName syntax everywhere.

Given <RDF><foo>A</foo><em:bar>B</em:bar></RDF>, persists the values read from the tags in grunt.option, then retrieves them in value callbacks in order to swap them in the resulting XML: <RDF><foo>B</foo><em:bar>A</em:bar></RDF>)

grunt.initConfig({
  xmlstoke: {
    rebuildScreensTag: {
      options: {
       namespaces: { 'em': 'http://www.mozilla.org/2004/em-rdf#' },
        actions: [{
          type: 'R'
          xpath: '//foo',
          saveAs: 'myFoo'
        }, {
          type: 'R'
          xpath: '//em:bar',
          saveAs: 'myBarAsArray',
          returnArray: true
        }, {
          xpath: '//em:bar',
          value: function (node) { return grunt.option('myFoo'); }
        }, , {
          xpath: '//foo',
          value: function (node) { return grunt.option('myBarAsArray')[0]; }
        }]
      },
      files: {
        'dest/output.xml': 'src/test.xml',
      },
    },
  },
})

Release History

  • 0.1.0 — Initial release
  • 0.2.0 — Multiple replacements at once
  • 0.2.1 — Color filename when logged
  • 0.3.0 — Allow specifying replacement value as a function
  • 0.4.0 — Allow specifying namespaces
  • 0.5.0 (point of fork from grunt-xmlpoke) — Allow adding elements or attributes via insertions option
  • 0.5.1 — Bugfixes
  • 0.5.2 — Allow [index] selection for insertion xpath (stripped from name for actual element creation) — Allow removing elements via deletions option (barely tested)
  • 0.6.0 — Code cleanup
  • 0.7.0 — Fixed deleting attribute nodes — Added updates option as an alias for replacements — Added reads option to extract node values by xpath and save them to grunt.option — Added actions option as a series of CRUD actions in arbitrary order — Added tests for deletions, reads and new alias parameters TODO: Add tests for expected error scenarios