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

xmlpoke

v0.1.16

Published

Module for modifying XML files.

Downloads

7,414

Readme

xmlpoke

npm version build status Dependency Status npm downloads

Node module for modifying XML files. Inspired by NAnt XmlPoke.

Install

$ npm install xmlpoke --save

Usage

The xmlpoke module exports a function that modifies xml files:

xmlpoke(path1, [path2], [...], [pathMap], modify)

Or modifies an xml string:

xmlpoke(xml, modify)

Paths

Paths are globs and can be any combination of strings, objects with a property that contains the path, or arrays of those. By default, objects are assumed to have a path property (Although this can be overridden). Here are some examples of valid path input:

var xmlpoke = require('xmlpoke');

xmlpoke('**/*.xml', ...);

xmlpoke('**/*.xml', '**/*.config', ...);

xmlpoke([ '**/*.xml', '**/*.config' ], ...);

xmlpoke([ '**/*.xml', '**/*.config' ], { path: '*.proj' }, ...);

xmlpoke([ '**/*.xml', { path: '*.proj' } ], '**/*.config', ...);

xmlpoke([ 'data/*.xml', { path: '*.proj' } ], [ '**/*.config', { path: '*.xml' } ], ...);

As noted earlier, path objects are expected to have a path property. If you would like to override that you can pass in a function, after your paths, that map the path:

var projects = [ 
    { 
        path: '/some/path', 
        config: 'app.config' 
    }, 
    ... 
];

xmlpoke(projects, function(p) { return path.join(p.path, p.config); }, ...);

Modification

The last argument is a function that performs the modifications. This function is passed a DSL for manipulating the xml file.

xmlpoke('**/*.config', function(xml) { 
    xml.set('data/connString', 'server=oh;db=hai');
});

var xml = xmlpoke('<oh/>', function(xml) { 
    xml.set('oh', 'hai');
});

Path objects, when supplied, are passed in the second argument:

var projects = [ 
    { 
        path: 'app.config', 
        version: '1.2.3.4' 
    }, 
    ... 
];

xmlpoke(projects, function(xml, project) { 
    xml.set('app/version', project.version);
});
Clearing Child Nodes

You can clear the children of all matching nodes with the clear() method:

xmlpoke('**/*.config', function(xml) { 
    xml.clear('some/path');
});

The errorOnNoMatches option will cause this method to throw an exception if the specified XPath yields no results.

Removing Nodes

You can remove all matching nodes with the remove() method:

xmlpoke('**/*.config', function(xml) { 
    xml.remove('some/path');
});

The errorOnNoMatches option does not cause this method to throw an exception if the specified XPath yields no results.

Ensuring the Existence of Elements

You can ensure the existence of elements with the ensure(xpath) method. This method will create the entire path. In order to do this the path must contain only elements. It must not contain any axis specifiers. Element axes can have a predicate that compares the equality of one or more attributes or elements with a string value. Only the = comparison operator and and logical operators are allowed. If these predicates are specified, and the element does not exist, the predicate values will be set in the new element. Any predicates that do not meet these exact requirements are ignored. The following are examples of acceptable XPaths and the resulting xml when the source is <el1/>:

ensure('el1/el2/el3');
<el1>
    <el2>
        <el3/>
    </el2>
</el1>
ensure("el1/el2[@attr1='1' and @attr2='2']/el3[el4='3']");
<el1>
    <el2 attr1="1" attr2="2">
        <el3>
            <el4>3</el4>
        </el3>
    </el2>
</el1>
Setting Values and Content

You can set the values or content of all matching nodes with the set(), add() and setOrAdd() methods. These methods take an XPath and a value or an object with XPath and value properties:

set([xpath, value]|[object])

add([xpath, value]|[object])

setOrAdd([xpath, value]|[object])

xmlpoke('**/*.config', function(xml) { 
    xml.set('some/path', 'value')
       .add('some/path', 'value')
       .setOrAdd('some/path', 'value')
       .set({ 'first/path': 'value1', 'second/path': 'value2' })
       .add({ 'first/path': 'value1', 'second/path': 'value2' })
       .setOrAdd({ 'first/path': 'value1', 'second/path': 'value2' });
});

The set() method expects all elements and attributes in the XPath to exist. If they do not, they will be ignored by default. To throw an exception specify the errorOnNoMatches option.

The add() method will create a new target node regardless of if there is a match. As such, this method is not very useful for attributes unless you are sure it doesn't already exist. On the other hand the setOrAdd() method will attempt to create the node if it doesn't exist, then set its value or content. This will not create the entire XPath as ensure() does, only the target element or attribute i.e. the last node in the XPath query. So the parent XPath must exist otherwise it will be ignored by default (To instead throw an exception, specify the errorOnNoMatches option). To be created, the target must be an attribute or an element. Element XPaths can have a predicate that compares the equality of one or more attributes or elements with a string value. Only the = comparison operator and and logical operators are allowed. If these predicates are specified, and the element does not exist, the predicate values will be set in the new element. Any predicates that do not meet these exact requirements are ignored. The following are examples of acceptable XPaths and the resulting xml when the source is <el1/>:

setOrAdd('el1/@attr', 'value');
<el1 attr="value"/>
setOrAdd('el1/el2', 'value');
<el1>
    <el2>value</el2>
</el1>
setOrAdd("el1[el2='value1']/el3[@attr1='value2' and @attr2='value3']", 'value4');
<el1>
    <el2>value1</el2>
    <el3 attr1="value2" attr2="value3">value4</el3>
</el1>

Values can be strings, CData, raw xml, a function or an object containing multiple attribute and element values. For example:

xmlpoke('**/*.config', function(xml) { 

    // Simple string value
    xml.set('some/path', 'value');

    // CData value
    xml.set('some/path', xml.CDataValue('value'));

    // Raw xml
    xml.set('some/path', xml.XmlString('<el attr="value">hai</el>'));

    // Function
    xml.set('some/path', function(node, value) { return 'value'; });

    // XPath and object with element and attribute values
    xml.set('some/path', {
        '@attr': 'value',
        el1: 'value',
        el2: xml.CDataValue('value'),
        el3: xml.XmlString('<el attr="oh">hai</el>'),
        el4: function(node, value) { return 'value'; }
    });

    // Object
    xml.set({
        'some/path/@attr': 'value',
        'some/path/el1': 'value',
        'some/path/el2': xml.CDataValue('value'),
        'some/path/el3': xml.XmlString('<el attr="value">hai</el>'),
        'some/path/el4': function(node, value) { return 'value'; },
        'some/path/el5': {
                '@attr': 'value',
                el1: 'value',
                el2: xml.CDataValue('value'),
                el3: xml.XmlString('<el attr="oh">hai</el>'),
                el4: function(node, value) { return 'value'; }
            }
        });
});

The CDataValue() and XmlString() methods exposed by the DSL are simply convenience methods for creating CDataValue and XmlString objects. These constructors are defined on the module and can be created directly as follows:

var xmlpoke = require('xmlpoke');

var cdata = new xmlpoke.CDataValue('value');
var xmlstring = new xmlpoke.XmlString('<el attr="value">hai</el>');

xmlpoke('**/*.config', function(xml) { 
    xml.set('some/path', cdata);
    xml.set('some/path', xmlstring);
});

Options

Base XPath

A base XPath can be specified so you do not have to specify the full XPath in following calls:

xmlpoke('**/*.config', function(xml) { 
    xml.withBasePath('configuration/appSettings')
       .set("add[@name='key1']", 'value1')
       .set("add[@name='key2']", 'value2');
});
Namespaces

Namespaces can be registered as follows:

xmlpoke('**/*.config', function(xml) { 
    xml.addNamespace('y', 'uri:oh')
       .addNamespace('z', 'uri:hai')
       .set("y:config/z:value", 'value');
});
Empty Results

By default, XPaths that do not yield any results are quietly ignored. To throw an exception instead, you can configure as follows:

xmlpoke('**/*.config', function(xml) { 
    xml.errorOnNoMatches()
    ...;
});

Contributors

| Raphael von der Grün | |:---:| | Raphael von der Grün |

License

MIT License