Syntax for HTML Island in Script

I still stand that adding a bunch of CFML block inside a script block is a Bad Idea ™ because it will be abused and we cant take it back.

It is not an attack Igal, it is a discussion.

sigh

I hate arguments of the way I said something.

I meant to say

“We have to decide what the function/syntax WOULD allow you to parse”

That better?

It’s awesome!

Consistent with what I wrote 25 messages ago at https://lucee.daemonite.io/t/syntax-for-html-island-in-script/2347/48?u=21solutions

render seems to output, not return the rendering, although it does seem to return a string - but just a blank one?

without getting into the merits of the whole endevour, this works in lucee 4.5 today:

<cfscript>
	//requires `this.mappings["/ram"] = "ram://";` in Application.cfc
	function tagIsland (input) {
		var filename = createUUID() & ".cfm";
		fileWrite("ram://" & filename, input);
		savecontent variable="local.output" {
			include template="/ram/" & filename;
		}
		fileDelete("ram://" & filename);
		return local.output;
	}

	y = "foo";
	x = tagIsland('<cfdump var="#y#" />');
	dump(x);
	writeoutput(x);
</cfscript>

You could make something like this more user friendly through an extension right? You could allow another way to delimit a multi-line string like using backticks (which would help in other places that there are escaping issues today) and that would make this just a little bit easier.

Yeah

See https://lucee.daemonite.io/t/add-optional-flag-to-render-to-buffer-the-output-and-return-as-string/2362

Thought crimes aside :police_car:

  • some people say all tags are bad, this position can’t be changed
  • some people say tags are useful sometimes, this position is fair
  • some people say using tags inline in a CFC would provide some benefits, rather than being forced to use a tag based CFC only, and this position is also fair
  • CFML is characterised by both Script and Tags, and this is life

I believe this boils down to whether or not:

  • there is a section of the community that wants the feature – sounds like there is
  • we get a Pull Request to build this – sounds like @21Solutions would be willing, and;
  • LAS is happy to accept the additional overhead of keeping it tested, documented and supported over the longer term – sounds minimal based on the current discussion

Absolutely agree.

I don’t understand why we wouldn’t allow for all tag parsing; ie. HTML and CFML. Especially if the more functional implementation is simpler to build and maintain.

Point taken. A few things though:

  • this suggestion makes mixing tags and script less awkward not more awkward
  • it’s already possible to abuse CFML with abandon; denying users who want this feature is not going to prevent more abuse
  • no “5 Tagger” worth his salt is going to build a script based CFC just so they can break open a tag island to commit self-harm
  • we’re all adults; if someone wants to run with scissors :scissors::sob: let them run


UPDATE:

Forgot to note that @joe.gooch suggestion of a potential migration method is great. I’ve seen this done in the past by isolating tag code into includes and bringing them into a component, but in practice this completely conceals what is going on in the external file and so gradual optimisations into script become less likely.

2 Likes

Script Blocks only have parts of the code
@bdw429s sadly this

<cfscript>
for(...) {
</cfscript>
<h1>#name#</h1>
<cfscript>
}
</cfscript>

sadly is not that easy. in that case we would have 2 complete indepedent script blocks. the only thing i can think of, is that an evaluator detect this situation and makes some magic.
In my opinion that would be the best solution by far.

Find the end
things like
cftag {<br>}
or
cftag(<br>)

Is not possible, because the parser cannot handle it, this could would be impossible for the parser to handle correctly.

cftag {
<script language="javascript">
function test(msg) {
   alert(msg);
}
</script>
}

Parser will not have a clue which } to pick as the end.
Lucee has 3 parsers, expression, script and tag parser. When the tag parser comes to <cfscript> it does not understand the content of cfscript, for the tag parser the body of the tag simply is a string. only the script tag itself then knows to handle it’s body because of this. so in short you need to find the end without understand the body. Because a different parser handles the body. The same is the case for a tag island. This is how the parser are working.

So we need to have a clear end that is not used in most cases by accident inside the code island, so } not really is an option.

1 Like

I agree and that makes sense for the intended purpose (a HTML tag island) - the unintended consequences of allowing CFML inside script however far outweigh any proposed advantages that I’ve seen discussed here.

Why wouldn’t it? If you can’t write a CFML tag island inside of a cfscript, then you cannot abuse the language in ways which are currently not possible. Therefore ‘denying’ users this ability would absolutely prevent more abuse.

No "5 Tagger" worth his salt is itself an oxymoron. I have never met one. You’re either an experienced developer who understands how to use the language to solve real world problems, or you’re a 5 tagger. EDIT: I count junior developers among those I consider experienced, just to be clear here.

That said, why wouldn’t a 5 tagger, having learned this capability exists, not immediately find a use case for putting tags into script?

Hey, this piece of cfscript code does almost what I want it to do, but I have no idea how to even modify it. Oh look, I can just use this tag island thingy to modify this function and it’ll do what I want.

Oh the glorious code that will be built if CFML tags are allowed inside of cfscript. I can see it now. ::shudder::

Correct, and as the adults in the room it is our job to help decide if giving the children scissors and letting them run with them is also appropriate. From where I sit, giving 5 taggers (the children in this analogy) scissors and telling them to run is a) bad language stewardship, b) shooting ourselves in the foot to spite our face and c) ignoring our responsibility to junior developers.

What I would like to see are a couple of things… not just single line examples. But big files of code that would be improved by adding this.

And the point of it. Rather than we can because reasons. But what we are aiming to solve.

I was thinking about this on the way and (pardon the half-baked thought) if there was a point to this it would be for specific parsers (not executers) for various… well parsers. What I mean is have things such as:

var html = html {

 <h1>#Title#</h1>
}

and then you can do other types of parsing:

var out = markdown {
# #Title#
The thing about the thing
}

or even

var sql = sql {
 SELECT * FROM Users Where Name = 'Bobby drop tables'
}

or

var js = ecmascript {

console.log("#output#") ;
}

With the ability to register a new parser if required.

This is for the engine to parse the code inside it… so I guess you could create a meta parser which would be:

var out = cfml{
<cfif mustWriteBadCodeToMakeCFMLLookBad>
CFML Rocks! No, really. IT does! Stop pulling that face. I am being serious. 
</cfif>
}

Anyway, just a half formed thought but yeah,. I could see some really fugly code in the coming years \o/

2 Likes

This raises the issue of how would this work in a cfscript CFC, where we cannot delineate what is script from what is not script.

Fair point. That takes any combination of (), [] and {} off the table for sure, I can’t think of anyone that would want to escape any of those on a regular basis.That leaves only the three backticks out of the proposed options, which would be problematic for anyone wishing to parse markup inside a tag island. The only other options I can think of are:

~~~ <h1>#somevar#</h1> ~~~

or

^^^ <h1>#somevar#</h1> ^^^

Those combinations seem unlikely in most use cases I think?

Agreed.

The reason I proposed the three backticks is that it’s common in MarkDown and in all of the “chat” apps out there (e.g. Slack).

Also, while ~~~ and ^^^ are unlikely, those characters are still used more commonly than the backticks, usually for “text decoration”.

Yeah, hadn’t thought about that. Also a fair point. Well, that leaves backticks unless we can come up with anything better.

I like the latest idea from @markdrew as well, it solves for some of the problem of using {} by setting expectations for what is allowed inside each parser type, of which } is less likely to occur inside plain HTML. ecmascript{} still suffers the same problem with }, however, and writing all the different parser’s would be more work.

We can do a combination of backticks with a cfml comment, e.g. (I’m using single quotes in the example for readability, or else discourse will mess up my format, but do a mentalReplace(snippet, “'”, “`”):

'''<!--- sql !---> SELECT * FROM some_table; '''

'''<!--- javascript !---> console.log("Hello from JavaScript!"); '''

`‘’'<!— html !—>

Some HTML markup.

'''`

'''<h1>Default, processed at <em>#now()#</em>.</h1>'''

This is similar to Github’s way of specifying a language:

The idea here is to make it easier for syntax hilighters to parse the snippet properly, but there is also a possibility to use it in the Lucee parser, and that way differentiate between cfml and non-cfml.

Why not a singe back-tick? In ES6 those define template literals. And no one uses back-ticks in their code (or it would be highly unlikely)

But still, if you have the idea of a back-tick to define a template literal you then need to say what you can parse inside it. The point I am getting at is I would hate to see this:

<cfscript>
cfml = `

10000's of lines of CFML

`
echo(cfml);
</cfscript>

Exactly because ES6 uses it. If we use a single backtick for the “tag island” (I think that “Literal Island”, or even “Template Literal” as in ES6 is a better term), then you will not be able to place ES6 template literals inside the island.

Or emulate PHP/ASP type constructs:

sql{?
    SELECT *
    FROM some_table;
?}
javascript{?
    console.log("Hello from JavaScript!");
?}
html{?
<h1>Some <em>HTML</em> markup.</h1>?}

{?<h1>Default, processed at <em>#now()#</em>.</h1>?}

{? block ?} keeps things separate from “normal” braces.

But then we have to define the “known” syntaxes, like sql, javascript, html, etc. in our parser.

Using my suggestion above you can put in the comment whatever you want, and future syntaxes will not require changes to the Lucee code. Only the editor parsers will have to know how to hilight them.

A more elaborate example using your notation to consider:

// example of sql use
sql = ```<!--- sql !--->
	SELECT colOne, colTwo
	FROM someTable
	WHERE colThree = :colThree
	AND colFour = :colFour
```

// example getting a query
qGetQuery = queryExecute( sql, { colThree = 'foo', colFour = 'bar' } );

// checking for records
if( qGetQuery.recordCount ) {
	// outputting html wrapper header
	```<!--- html !--->
	<div>
		<select name="someName" id="some-id">
			<option value="someValue">someLabel</option>
	```

	// looping through records
	for( record in qGetQuery ) {
		// outputting option
		```<!--- html !--->
			<option value="#record.colOne#">#record.colTwo#>
		```
	}

	// outputting html wrapper footer
	```<!--- html !--->
		</select>
	</div>
	<div id="other-id"></div>
	```

    // outputting javascript
	```<!--- javascript !--->
	$( function() {
		$('#some-id').on( 'change', function() {
			$('#other-id')
               .html( '<h1>You selected option: ' + $(this).val() + '</h1>' );
		});	
	});
	```
}
1 Like

Nice!

Since <!--- html !---> will be default, and the language hints are optional anyway, just showing this in a more concise way:

for( record in qGetQuery ) {
	```<option value="#record.colOne#">#record.colTwo#</option>```
}

Also, as mentioned above, the ES6 Template Literals can now be used safely:

```<!--- javascript !--->
    $(function() {
        $('#some-id').on('change', function() {
            var value = $(this).val();
            $('#other-id').html(`<h1>Selected option: ${value}</h1>`);
        });
    });
```
1 Like