Project

Profile

Help

Bug #6374

open

saxon.compile removed in 2.6+?

Added by Jai B 8 months ago. Updated 8 months ago.

Status:
New
Priority:
Normal
Assignee:
-
Category:
API
Sprint/Milestone:
Start date:
2024-03-19
Due date:
% Done:

0%

Estimated time:
Applies to JS Branch:
Fix Committed on JS Branch:
Fixed in JS Release:
SEF Generated with:
Platforms:
Company:
-
Contact person:
-
Additional contact persons:
-

Description

Hi,

I've always used the saxon.compile function to generate sef files in code. I know it's undocumented but IIRC it was shown in the command line docs or something.

Basically I cache the compiled sef so I don't have to recompile everytime I want to do a transform and I don't want to pre-transform my XSLT files on the command line, nor do I have any desire to use the Java version for that part, the JS compiler has worked amazingly!

My web app does server side transformations with node; anytime it sees the XSLT change, it runs a compile to generate and cache a new sef to use for subsequent transformations. I can't cache the result of the transformation either since the data is dynamic, of course.

Perhaps I'm missing something or missing some changes, but this has worked awesome for years and only just noticed things breaking when redeploying some stuff and npm updating to 2.6.0 and getting an error that the function doesn't exist.

When I was initially implementing this, I know I spent a fair bit of time trying to figure this part out and ensure I was doing things right; I think I scoped the code that generates the sef on command line to find the function in the first place and was surprised it wasn't actually documented/available anyway.

Thanks again for SaxonJS!

Actions #1

Updated by Norm Tovey-Walsh 8 months ago

I've always used the saxon.compile function to generate sef files in code. I know it's undocumented but IIRC it was shown in the command line docs or something.

Yes, it was undocumented (I’m not sure I even knew it existed). And a user reported that the function didn’t work correctly with xsl:imports[1].

It turned out that it was undocumented at least in part because the design was incomplete. Rather than leaving an undocumented, broken function exposed, we took it out.

I think it’s very likely that the function will return, with a documented and tested API, but I can’t say exactly when.

Be seeing you,
norm

[1] https://saxonica.plan.io/issues/6101

--
Norm Tovey-Walsh
Saxonica

Actions #2

Updated by Jai B 8 months ago

Norm Tovey-Walsh wrote in #note-1:

I've always used the saxon.compile function to generate sef files in code. I know it's undocumented but IIRC it was shown in the command line docs or something.

Yes, it was undocumented (I’m not sure I even knew it existed). And a user reported that the function didn’t work correctly with xsl:imports[1].

It turned out that it was undocumented at least in part because the design was incomplete. Rather than leaving an undocumented, broken function exposed, we took it out.

I think it’s very likely that the function will return, with a documented and tested API, but I can’t say exactly when.

Be seeing you,
norm

[1] https://saxonica.plan.io/issues/6101

--
Norm Tovey-Walsh
Saxonica

I'm not sure what that users issue was, but I've had no issues at all using it with xsl imports, in fact I use them quite a bit!

<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
	xmlns:ff="http://www.shaped.ca/freeform"
	version="1.0">

<xsl:output
	method="xhtml"
	indent="yes"
	encoding="utf-8"
/>

<xsl:strip-space elements="*"/>

<xsl:import href="_inc.xsl" />

<xsl:template name="default" match="/">
<xsl:choose>
	<xsl:when test="ff[@chunked='true']">
		<xsl:call-template name="chunkedBase" />
	</xsl:when>
	<xsl:otherwise>
		<xsl:call-template name="defaultBase" />
	</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>

where _inc.xsl is

<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
	xmlns:ff="http://www.shaped.ca/freeform"
	version="1.0">

<xsl:import href="bindings.xsl" />

<xsl:import href="base/_inc.xsl" />
<xsl:import href="web/_inc.xsl" />
<xsl:import href="pwa/_inc.xsl" />

</xsl:stylesheet>

where all of those _inc.xsl's are more imports.. :)

In my opinion, it's a serious key piece of the puzzle of using SaxonJS. Of course, if you need more advanced features or are perhaps already using Java elsewhere (with or without XSLT) then compiling with that may be better but I've not run into an instance yet where I've needed any functionality that wasn't provided by the JS compiler.

As I said, my app automatically compiles, caches the result anytime an XSLT file is updated and uses that to transform templates. I'm an avid hater of build processes (stuff like webpack boils my blood) and having to run a CLI build step anytime I change a template during development would be a huge productivity killer, likewise implementing the equivalent with node doing an exec of some sort to use the CLI version (or, at this point, diving back in to figure out where the code the CLI method uses to compile is now hidden) would be as well.

My process is literally: change XSLT template (or even SCSS and others), press ctrl-s, press alt-tab, press ctrl-r to refresh page. I don't know how others can sit there and wait for a build process, I even tried the hot rebuild/dev mode of webpack once and it was so slow I couldn't bear it. At most I wait like 1ish seconds for the XSLT to recompile while it's reloading.

I of course appreciate the desire to not have undocumented and/or broken components exposed; however, this is indeed (undocumented or not) a breaking change without compatibility provided; not sure if you guys adhere to semver versioning, but such a change should only happen in a major version number change, eg. 2.x to 3.x not 2.5 to 2.6.

Admittedly I seem to have a penchant for hooking into undocumented features, not that I mean to - for ages I was using a PHP-based backend and when I started to feel PHP was becoming a bit old and crusty I started re-implementing the template handling in JS nearly 1:1 as it was in PHP so I really didn't think I was doing anything particularly unique or that would need undocumented feataures.

Previously I was using a generated and undocumented property to implement a custom element which includes a component (sorta similar to XSL imports but also loads a JS module to generate/populate the data used for transformation) - I did make a ticket about it, as I didn't initially connect that the property name would change between versions due to it being generated and I was having to update my XSLT processing code every version to reflect that and you guys kindly exposed the property in 2.4.0 which was awesome and I hugely appreciate it, with a happy comment still existing in my code today:

let ffComponents=[];
let ffComponentNodes = this.saxon.XPath.evaluate('//ff:component/@name',doc,this.XPathOptions);

for (let x=ffComponentNodes.next();x!=null;x=ffComponentNodes.next()) {
	ffComponents.push({
		name:x.value,
		node:x.parent // oo? saxon fixed on 2.4.0!! yay no more!?
	});
}

Previously, x.parent was like x.kL or something.

As for XSL imports (and includes?), this is how I implemented it, I'm not sure how it compares to what the user who had an issue was doing (and it's been a few years since I wrote this but has worked solid):

async checkXSLImports(doc, xslPath = '') {
	let xslImports = [];

	let importNodes = this.saxon.XPath.evaluate('//xsl:import/@href',doc,this.XPathOptions);
	let includeNodes = this.saxon.XPath.evaluate('//xsl:include/@href',doc,this.XPathOptions);

	for (let x=importNodes.next();x!=null;x=importNodes.next()) xslImports.push(x.value);
	for (let x=includeNodes.next();x!=null;x=includeNodes.next()) xslImports.push(x.value);

	for (let xslImport of xslImports) {
		let importStat;
		try {
			importStat = fs.statSync(`${this.templatePath}${xslPath}${xslImport}`);
		} catch(e) {
			// error logging code here removed for brevity in case anyone's curious why the error is seemingly needlessly caught and rethrown :-)
			throw new Error(e);
		}

		if (importStat && importStat.mtimeMs > this.sefStat?.mtimeMs) {
			return true;
		} else {
			let path = xslImport.split('/');
			let importPath = '';

			for (let x=0;x<path.length-1;x++)
				if (x!=path.length-1)
					importPath += path[x]+"/";

			let nestedDoc = await this.saxon.getResource({file:`${this.templatePath}${xslPath}${xslImport}`, type:'xml'});
			let result = await this.checkXSLImports(nestedDoc, importPath);

			if (result) return true;
		}
	}

	return false;
}

Again, I haven't dug into that in a while so perhaps I'm handling some things here the other user perhaps expected to not have to handle? Feel free to provide my example for that user to use (or anyone else needing xsl:import) if it might be useful for them, though of course they'd also have to stick with <2.5.0 for now but if it helps anyone then that's great.

Anyhow; I'm not sure there's any new stuff or fixes I need in the new version, in fact, since it generally works for me I'm going to avoid looking at the changelog so I'm not tempted by anything there and will just pin at 2.5.0 for now.

As always, appreciate your work and the availability of SaxonJS. I've been using XSLT templates for at least nearly or over 20 years now (I been making webpages longer than google been google, lol) and while a lot of my use is perhaps not as complex as is possible, it's always served great even just using it as a templating engine, I truly wish XSLT had become more popular and widely implemented being that it's a standard compared to common things like JSX/TSX, Mustache, Go Templates, etc. Would be awesome if we really could just use browser implementations without any worries and get the full functionality but at least SaxonJS exists instead!

If you could be so kind as to keep this ticket open and update it in the future when the method has been updated and returned I would much appreciate it as it gives me a place where I can easily check back later.

Thanks again! Keep up the great work!

Please register to edit this issue

Also available in: Atom PDF Tracking page