Jump to content

XML output from JavaScript Script Filter logs error


Recommended Posts

Hi,

 

The XML I'm outputting from my Javascript Script Filter is causing an error by I can't figure out what the issue is.

 

I have similar code working fine as a Ruby Script Filter. It takes a string input e.g. "this is an example", transforms it to "this-is-an-example" and outputs the XML so it can be copied to the clipboard.


 

I've tried to convert it to use the JS Script Filter but it gives the following error in the workflow debugger and doesn't trigger the action in Alfred:




Starting debug for 'filename'

[STDERR: alfred.workflow.input.scriptfilter] <?xml version="1.0"?>
<items>
<item valid="YES" autocomplete="example-input">
<title>example-input</title>
<text type="copy">example-input</text>
<text type="largetype">example-input</text>
</item>
</items>


 

Here's the Ruby script that works correctly:

 

Language: /usr/bin/ruby

Behaviour: Terminate previous script

Queue delay: Immediately after every character types

 

Script:



filename = "{query}".downcase.gsub(' ', '-')

puts(<<-EOT)
<?xml version="1.0"?>
<items>
<item valid="YES" autocomplete="#{filename}">
<title>#{filename}</title>
<text type="copy">#{filename}</text>
<text type="large type">#{filename}</text>
</item>
</items>
EOT


 

And this is the JS equivalent that doesn't work:

 

Language: /usr/bin/osascript (JS)

Behaviour: Terminate previous script

Queue delay: Immediately after every character types

 

Script:



var filename = "{query}".toLowerCase().replace(/ /g, '-');

var xml = [
'<?xml version="1.0"?>',
'<items>',
'<item valid="YES" autocomplete="#{filename}">',
'<title>#{filename}</title>',
'<text type="copy">#{filename}</text>',
'<text type="large type">#{filename}</text>',
'</item>',
'</items>'
].join('\n').replace(/#\{filename\}/g, filename);

console.log(xml);


 

Any ideas what I'm doing wrong? Happy to post any more debugging info if needed.

 

Thanks

Link to comment

The answer is in the first line of the log output:

[STDERR: alfred.workflow.input.scriptfilter]
Your JavaScript version is sending the XML to STDERR, not STDOUT.

 

Unfortunately, I can't tell you how to fix it (other than don't use console.log()), as I don't have the first clue about using JavaScript outside of a browser (why would you? It's a terrible language, and Ruby is waaaay better).

 

FWIW, inserting variables into text is a bad way to generate XML: if a character like & or " is in filename (and likely any non-ASCII character, too, due to encoding issues), your XML will be invalid and the workflow won't work. Much better to use a real XML library to generate your XML, and make sure it's UTF-8 encoded.

I'd advise you to stick with Ruby and use one of its libraries to generate the XML.

Edited by deanishe
Link to comment
 

I'm using this example as a way of trying out the OSAScript JS integration in the latest Alfred release.

 

Thanks to your help, I've figured out that in Apple's JS OSAScript host, console.log outputs to stderr.

 

A convoluted way to output to stdout seems to be:



ObjC.import('stdio');
$.printf('this string outputs to stdout');


So, I can get the script working with this:



ObjC.import('stdio');

var filename = '{query}'.toLowerCase().replace(/ /g, '-');

var xml = [
'\n<?xml version="1.0"?>',
'<items>',
'\t<item valid="YES" autocomplete="#{filename}">',
'\t\t<title>#{filename}</title>',
'\t\t<text type="copy">#{filename}</text>',
'\t\t<text type="largetype">#{filename}</text>',
'\t</item>',
'</items>'
].join('\n').replace(/#\{filename\}/g, filename);

$.printf(xml);


However, printf returns an int of the number of chars in the string and so Alfred now complains about this:



[ERROR: alfred.workflow.input.scriptfilter] XML Parse Error 'The operation couldn’t be completed. (NSXMLParserErrorDomain error 5.)'. Row (null), Col (null): 'Extra content at the end of the document' in XML:
<?xml version="1.0"?>
<items>
<item valid="YES" autocomplete="example-input">
<title>example-input</title>
<text type="copy">example-input</text>
<text type="largetype">example-input</text>
</item>
</items>215

Edited by andrewn
Link to comment

I've had a quick play with JavaScript and Script Editor.
 
What you're seeing isn't the return value of $.printf() per se, but the result of the script. So instead of using $.printf(), just write xml;:

var filename = '{query}'.toLowerCase().replace(/ /g, '-');

var xml = [
'<?xml version="1.0"?>',
'<items>',
'\t<item valid="YES" autocomplete="#{filename}">',
'\t\t<title>#{filename}</title>',
'\t\t<text type="copy">#{filename}</text>',
'\t\t<text type="largetype">#{filename}</text>',
'\t</item>',
'</items>'
].join('\n').replace(/#\{filename\}/g, filename);

xml;

When run, this outputs:

<?xml version="1.0"?>
<items>
	<item valid="YES" autocomplete="{query}">
		<title>{query}</title>
		<text type="copy">{query}</text>
		<text type="largetype">{query}</text>
	</item>
</items>

Note, I've removed the stdio import and the preceding newline in the XML output.
 
A better way (i.e. better coding practice) is to define a run function, which will be called when the script is run:

// Called automatically when the script is run.
// Its return value will be output to STDOUT.
function run(argv) {
	var filename = '{query}'.toLowerCase().replace(/ /g, '-');
	return generateXml(filename);
}

function generateXml(filename) {

	var xml = [
		'<?xml version="1.0"?>',
		'<items>',
		'\t<item valid="YES" autocomplete="#{filename}">',
		'\t\t<title>#{filename}</title>',
		'\t\t<text type="copy">#{filename}</text>',
		'\t\t<text type="largetype">#{filename}</text>',
		'\t</item>',
		'</items>'
	].join('\n').replace(/#\{filename\}/g, filename);

	return xml;
}
Edited by deanishe
Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...