Created
November 13, 2016 19:59
-
-
Save ndemengel/13130b50ffbd9cbe6c1da335c1ad026b to your computer and use it in GitHub Desktop.
JSP pseudo-interpretation in JS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<p>should be shown 1</p> | |
<p>should be shown 2</p> | |
<p>should be shown 3</p> | |
<form modelAttribute="formModel"> | |
<input type="number" value="form model member value" id="formModelMember" name="formModelMember" data-plop="name 1"/> | |
<input type="date" value="toto"> | |
<input type="number" value="42"> | |
<input type="number" value="form model member value" id="formModelMember" name="formModelMember" data-plop="name 2"/> | |
<input type="date" value="toto"> | |
<input type="number" value="42"> | |
notavar | |
A VALUE TO &SCAPE | |
<!-- unsupported JSP tag removed: <un:supported jsp="tag"> --><!-- unsupported JSP tag removed: </un:supported> --> | |
<a title="i18n_message.code" href="#">Link</a> | |
<!-- @include sibling1: --> | |
<p>Text from sibling.jsp with some toto</p> | |
<dl> | |
<dt>Param 1</dt><dd>static</dd> | |
<dt>Param 2</dt><dd>dynamic, resolved</dd> | |
</dl> | |
<p>anewvar: anoldvalue</p> | |
<p>multilineSet: foo | |
bar</p> | |
<p>escapedSet: A VALUE TO &SCAPE</p> | |
<!-- jsp:include sibling1: --> | |
<p>Text from sibling.jsp with some toto</p> | |
<dl> | |
<dt>Param 1</dt><dd></dd> | |
<dt>Param 2</dt><dd></dd> | |
</dl> | |
<p>I support "empty"!</p> | |
<p>I support "not empty"!</p> | |
<p>I support "or" too!</p> | |
<input type="radio" name="someRadioButtonPath" value="1" id="someRadioButtonPath1"/> | |
<span>1</span> | |
<span>2</span> | |
<span>3</span> | |
<p>i18n_message.with.args,arg1,arg2</p> | |
<span>fooINCLUDEDVAR</span> | |
<span>INCLUDEDVARbar</span> | |
<span>fooINCLUDEDVARbar</span> | |
</form> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
const fs = require('fs'); | |
const path = require('path'); | |
const expect = require('./').expect; | |
const jsp = require('./jsp'); | |
describe('Jsp Translator', () => { | |
describe('<c:choose>', () => { | |
it('should work with only a "when" clause', () => { | |
// given | |
let attributes = {someCondition: true}; | |
const template = '<c:choose>' + | |
'<c:when test="${someCondition}">' + | |
'it is true' + | |
'</c:when>' + | |
'</c:choose>'; | |
// then | |
expect(jsp.resolve(template, attributes)).to.equal('it is true'); | |
// and given | |
attributes = {someCondition: false}; | |
// then | |
expect(jsp.resolve(template, attributes)).to.equal(''); | |
}); | |
it('should work with 1 "when" clause and 1 "otherwise" clause', () => { | |
// given | |
let attributes = {someCondition: true}; | |
const template = '<c:choose>' + | |
'<c:when test="${someCondition}">' + | |
'it is true' + | |
'</c:when>' + | |
'<c:otherwise>' + | |
'it is false' + | |
'</c:otherwise>' + | |
'</c:choose>'; | |
// then | |
expect(jsp.resolve(template, attributes)).to.equal('it is true'); | |
// and given | |
attributes = {someCondition: false}; | |
// then | |
expect(jsp.resolve(template, attributes)).to.equal('it is false'); | |
}); | |
it('should work with several "when" clauses and 1 "otherwise" clause', () => { | |
// given | |
let attributes = {someNumber: 1}; | |
const template = '<c:choose>' + | |
'<c:when test="${someNumber == 1}">' + | |
'one' + | |
'</c:when>' + | |
'<c:when test="${someNumber == 2}">' + | |
'two' + | |
'</c:when>' + | |
'<c:otherwise>' + | |
'other' + | |
'</c:otherwise>' + | |
'</c:choose>'; | |
// then | |
expect(jsp.resolve(template, attributes)).to.equal('one'); | |
// and given | |
attributes = {someNumber: 2}; | |
// then | |
expect(jsp.resolve(template, attributes)).to.equal('two'); | |
// and given | |
attributes = {someNumber: 42}; | |
// then | |
expect(jsp.resolve(template, attributes)).to.equal('other'); | |
}); | |
it('should work with string representation of "true"', () => { | |
// given | |
let attributes = {}; | |
const template = '<c:choose>' + | |
'<c:when test="true">' + | |
'it is true' + | |
'</c:when>' + | |
'<c:otherwise>' + | |
'it is false' + | |
'</c:otherwise>' + | |
'</c:choose>'; | |
// then | |
expect(jsp.resolve(template, attributes)).to.equal('it is true'); | |
}); | |
it('should work with string representation of "false"', () => { | |
// given | |
let attributes = {}; | |
const template = '<c:choose>' + | |
'<c:when test="false">' + | |
'it is true' + | |
'</c:when>' + | |
'<c:otherwise>' + | |
'it is false' + | |
'</c:otherwise>' + | |
'</c:choose>'; | |
// then | |
expect(jsp.resolve(template, attributes)).to.equal('it is false'); | |
}); | |
}); | |
describe('<c:forEach>', () => { | |
it('should loop over an array of items', () => { | |
// given | |
const attributes = { | |
someItems: [ | |
{prop1: 'a', prop2: 'b'}, | |
{prop1: 'c', prop2: 'd'}, | |
{prop1: 'e', prop2: 'f'} | |
] | |
}; | |
const template = '<c:forEach items="${someItems}" var="thing">' + | |
'<li>${thing.prop1} - ${thing.prop2}</li>' + | |
'</c:forEach>'; | |
// then | |
expect(jsp.resolve(template, attributes)).to.equal( | |
'<li>a - b</li>' + | |
'<li>c - d</li>' + | |
'<li>e - f</li>' | |
); | |
}); | |
it('should loop over a range of numbers', () => { | |
// given | |
const attributes = {}; | |
const template = '<c:forEach begin="2" end="5" var="num">' + | |
'<li>${num}</li>' + | |
'</c:forEach>'; | |
// then | |
expect(jsp.resolve(template, attributes)).to.equal( | |
'<li>2</li>' + | |
'<li>3</li>' + | |
'<li>4</li>' + | |
'<li>5</li>' | |
); | |
}); | |
it('should provide looping status for an array', () => { | |
// given | |
const attributes = { | |
someItems: ['A', 'B', 'C'] | |
}; | |
const template = '<c:forEach items="${someItems}" var="el" varStatus="loop">' + | |
'[${el},${loop.index},${loop.first},${loop.last}]' + | |
'</c:forEach>'; | |
// then | |
expect(jsp.resolve(template, attributes)).to.equal( | |
'[A,0,true,false]' + | |
'[B,1,false,false]' + | |
'[C,2,false,true]' | |
); | |
}); | |
it('should provide looping status for a range', () => { | |
// given | |
const attributes = { | |
someItems: ['A', 'B', 'C'] | |
}; | |
const template = '<c:forEach begin="100" end="102" var="el" varStatus="loop">' + | |
'[${el},${loop.index},${loop.first},${loop.last}]' + | |
'</c:forEach>'; | |
// then | |
expect(jsp.resolve(template, attributes)).to.equal( | |
'[100,0,true,false]' + | |
'[101,1,false,false]' + | |
'[102,2,false,true]' | |
); | |
}); | |
}); | |
describe('<c:if>', () => { | |
it('should work with a simple condition', () => { | |
// given | |
let attributes = {someCondition: true}; | |
const template = '<c:if test="${!someCondition}">' + | |
'it is true' + | |
'</c:if>'; | |
// then | |
expect(jsp.resolve(template, attributes)).to.equal(''); | |
// and given | |
attributes = {someCondition: false}; | |
// then | |
expect(jsp.resolve(template, attributes)).to.equal('it is true'); | |
}); | |
it('should work with string representation of "true"', () => { | |
// given | |
let attributes = {}; | |
const template = '<c:if test="true">' + | |
'it is true' + | |
'</c:if>'; | |
// then | |
expect(jsp.resolve(template, attributes)).to.equal('it is true'); | |
}); | |
it('should work with string representation of "false"', () => { | |
// given | |
let attributes = {}; | |
const template = '<c:if test="false">' + | |
'it is true' + | |
'</c:if>'; | |
// then | |
expect(jsp.resolve(template, attributes)).to.equal(''); | |
}); | |
}); | |
describe('<c:out>', () => { | |
it('should escape HTML in given value attribute', () => { | |
// given | |
let attributes = {someHtml: '<p>not a paragraph</p>'}; | |
const template = '<c:out value="${someHtml}"/>'; | |
// then | |
expect(jsp.resolve(template, attributes)).to.equal('<p>not a paragraph</p>'); | |
}); | |
}); | |
describe('<c:set>', () => { | |
it('should set a variable to a static value', () => { | |
// given | |
const attributes = {}; | |
const template = '<c:set var="someVar" value="someValue"/>' + | |
'${someVar}'; | |
// then | |
expect(jsp.resolve(template, attributes)).to.equal('someValue'); | |
}); | |
it('should set a variable to a dynamic value', () => { | |
// given | |
const attributes = {someVar: 'foo'}; | |
const template = '<c:set var="someOtherVar" value="A${someVar}B"/>' + | |
'${someOtherVar}'; | |
// then | |
expect(jsp.resolve(template, attributes)).to.equal('AfooB'); | |
}); | |
it('should set a variable using element content', () => { | |
// given | |
const attributes = {foo: 'bar'}; | |
const template = `<c:set var="someVar"> | |
A | |
\${foo} | |
B | |
</c:set> | |
\${someVar}`; | |
// then | |
expect(jsp.resolve(template, attributes)).to.equal(` | |
A | |
bar | |
B`); | |
}); | |
}); | |
// and so on... Skipped: tests for form:*, jsp:* spring:* | |
it('should translate a big soup of JSP tags', () => { | |
const attributes = { | |
bar: 'toto', | |
formModel: { | |
formModelMember: 'form model member value' | |
}, | |
myVar: 'someVal', | |
myVar2: 'someOtherVal', | |
things: [ | |
{name: 'name 1'}, | |
{name: 'name 2'} | |
], | |
avar: 'A VALUE TO &SCAPE', | |
anoldvar: 'anoldvalue', | |
emptyVar: null, | |
emptyVar2: '', | |
nonEmptyVar: "I'm not empty", | |
radioPath: "someRadioButtonPath", | |
one: 1 | |
}; | |
const jspPath = path.resolve(__dirname, './main.jsp'); | |
const expectedHtml = fs.readFileSync(path.resolve(__dirname, 'expected.html')).toString(); | |
expect(jsp.resolveFile(jspPath, attributes)).to.equal(expectedHtml); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
const fs = require('fs'); | |
const path = require('path'); | |
const _ = require('lodash'); | |
function unwrapElExpression(value) { | |
if (value === null || value === undefined) { | |
return ''; | |
} | |
if (value.indexOf('${') !== 0) { | |
if (value.indexOf('}') !== value.length - 1) { | |
return value.replace(/^(.*?)\$\{([^}]*?)}([^}]*?)$/, '"$1" + $2 + "$3"'); | |
} | |
return value.replace(/^(.*?)\$\{([^}]*?)}$/, '"$1" + $2'); | |
} | |
if (value.indexOf('}') !== value.length - 1) { | |
return value.replace(/^\$\{([^}]*?)}([^}]*?)$/, '$1 + "$2"'); | |
} | |
return value.replace(/^\$\{([^}]*?)}$/, '$1'); | |
} | |
function resolve(jsp, attributes, context) { | |
context = context || {jspDir: __dirname}; | |
const ATTRIBUTE = /(?:\s+?([\w-]+?=".*?"))/g; | |
const EL_EXPRESSION = /\$\{([^}]*?)\}/g; | |
const FORM_INPUT = /(<form:\w+\s+.*?\b)path=".*?"(.*?\/?>)/g; | |
const FORM = /(<\/?)form:/g; | |
const INCLUDE_DIRECTIVE = /<%@\s*include\s+file="(.*?)"\s*%>/g; | |
const JSP_DIRECTIVE_OR_COMMENT = /<%(@|--).*?(--)?%>/g; | |
const JSP_TAG = /<\/?(\w+:\w+)(\s+[^>]*?)?\s*\/?>/g; | |
function readAttributes(str) { | |
const attrs = {}; | |
let res; | |
while ((res = ATTRIBUTE.exec(str)) !== null) { | |
const kv = res[1].split('='); | |
const key = kv[0]; | |
let value = kv.slice(1).join('='); | |
if (value) { | |
value = value.replace(/^"(.*?)"$/, '$1'); | |
attrs[key] = replaceElOperators(value); | |
} else { | |
attrs[key] = null; | |
} | |
} | |
return attrs; | |
} | |
function replaceElOperators(jsp) { | |
let res; | |
const buf = []; | |
let idx = 0; | |
while ((res = EL_EXPRESSION.exec(jsp)) !== null) { | |
buf.push(jsp.substring(idx, res.index)); | |
buf.push('${', res[1].replace(/\bnot\s+empty\s+/g, '!!') | |
.replace(/\bempty\s+/g, '!') | |
.replace(/\s+and\s+/, ' && ') | |
.replace(/\s+or\s+/, ' || '), '}'); | |
idx = EL_EXPRESSION.lastIndex; | |
} | |
buf.push(jsp.substring(idx)); | |
return buf.join(''); | |
} | |
function replaceFormInputs(jsp) { | |
let res; | |
const buf = []; | |
let idx = 0; | |
while ((res = FORM_INPUT.exec(jsp)) !== null) { | |
buf.push(jsp.substring(idx, res.index)); | |
buf.push(res[1]); | |
const attrs = readAttributes(res[0]); | |
if (!attrs.id) { | |
buf.push(' id="', attrs.path, '"'); | |
} | |
if (!attrs.name) { | |
buf.push(' name="', attrs.path, '"'); | |
} | |
buf.push(res[2]); | |
idx = FORM_INPUT.lastIndex; | |
} | |
buf.push(jsp.substring(idx)); | |
return buf.join(''); | |
} | |
function resolveInclude(file, attrs) { | |
if ((context.ignoredInclusions || []).indexOf(file) !== -1) { | |
return ''; | |
} | |
let jspDir = context.jspDir; | |
if (!path.isAbsolute(file)) { | |
const fileRelDir = path.dirname(context.jspFile.substr(context.jspDir.length + 1)); | |
if (fileRelDir) { | |
jspDir = path.join(jspDir, fileRelDir); | |
} | |
} | |
if (file.startsWith('/WEB-INF/jsp/')) { | |
jspDir = context.jspRoot; | |
file = file.substr('/WEB-INF/jsp/'.length); | |
} | |
const filePath = path.resolve(jspDir, file); | |
return resolveFile(filePath, attrs, { | |
jspRoot: context.jspRoot, | |
ignoredInclusions: context.ignoredInclusions | |
}); | |
} | |
function replaceJspTags(jsp) { | |
let res; | |
let buf = []; | |
let bufBackup; | |
let idx = 0; | |
let currentJspParams, currentJspPageToInclude, currentChoose, currentModelAttribute, currentSpringArgs; | |
function jspInclude() { | |
const inclusionAttrs = _.assign({ | |
param: currentJspParams | |
}, attributes); | |
const str = resolveInclude(currentJspPageToInclude, inclusionAttrs); | |
currentJspPageToInclude = null; | |
currentJspParams = null; | |
return str; | |
} | |
const STRATEGIES = { | |
DEFAULT: { | |
open: (buf, res) => { | |
buf.push('<!-- unsupported JSP tag removed: ', res[0], ' -->'); | |
}, | |
close: (buf, res) => { | |
buf.push('<!-- unsupported JSP tag removed: ', res[0], ' -->'); | |
} | |
}, | |
'c:choose': { | |
open: () => { | |
currentChoose = {}; | |
}, | |
close: () => { | |
buf.push('<% } %>'); | |
} | |
}, | |
'c:otherwise': { | |
open: buf => { | |
buf.push('<% } else { %>'); | |
}, | |
close: _buf => { | |
} | |
}, | |
'c:when': { | |
open: (buf, res, attrs) => { | |
if (currentChoose.firstWhenPassed) { | |
buf.push('<% } else if (', unwrapElExpression(attrs.test), ') { %>'); | |
} else { | |
buf.push('<% if (', unwrapElExpression(attrs.test), ') { %>'); | |
} | |
currentChoose.firstWhenPassed = true; | |
}, | |
close: _buf => { | |
} | |
}, | |
'c:forEach': { | |
open: (buf, res, attrs) => { | |
let items; | |
if (attrs.items) { | |
items = unwrapElExpression(attrs.items); | |
} else { | |
items = '_.range(' + unwrapElExpression(attrs.begin) + ', ' + unwrapElExpression(attrs.end) + ' + 1)'; | |
} | |
if (attrs.varStatus) { | |
buf.push('<% var _forEachItems = ', items, '; ', | |
'var ', attrs.varStatus, ' = { begin: 0, end: _forEachItems.length - 1 }; ', | |
'_forEachItems.forEach(function (', attrs.var, ', _forEachIdx) { ', | |
attrs.varStatus, '.index = _forEachIdx;', | |
attrs.varStatus, '.first = _forEachIdx === 0;', | |
attrs.varStatus, '.last = _forEachIdx === _forEachItems.length - 1;', | |
'%>'); | |
} else { | |
buf.push('<% ', items, '.forEach(function (', attrs.var, ') { %>'); | |
} | |
}, | |
close: buf => { | |
buf.push('<% }); %>'); | |
} | |
}, | |
'c:if': { | |
open: (buf, res, attrs) => { | |
buf.push('<% if (', unwrapElExpression(attrs.test), ') { %>'); | |
}, | |
close: buf => { | |
buf.push('<% } %>'); | |
} | |
}, | |
'c:set': { | |
mayReadContent: true, | |
open: (buf, res, attrs) => { | |
if (typeof attrs.value === 'string') { | |
if (attrs.value.indexOf('${') !== -1) { | |
buf.push('<% var ', attrs.var, ' = ', unwrapElExpression(attrs.value), '; %>'); | |
} else { | |
buf.push('<% var ', attrs.var, ' = "', attrs.value, '"; %>'); | |
} | |
} else { | |
buf.push('<% var ', attrs.var, ' = '); | |
} | |
}, | |
close: (buf, res, setContent) => { | |
if (!setContent) { | |
return; | |
} | |
setContent = setContent.trim(); | |
if (setContent.indexOf('${') !== -1) { | |
if (setContent.indexOf('${') !== 0) { | |
if (setContent.indexOf('}') !== setContent.length - 1) { | |
buf.push(setContent.replace(/^([\S\s]*?)\$\{([^}]*?)}([^}]*)$/m, '`$1` + $2 + `$3`')); | |
} else { | |
buf.push(setContent.replace(/^([\S\s]*?)\$\{([^}]*)}$/m, '`$1` + $2')); | |
} | |
} | |
else if (setContent.indexOf('}') !== setContent.length - 1) { | |
buf.push(setContent.replace(/^\$\{([^}]*?)}([^}]*)$/m, '$1 + `$2`')); | |
} else { | |
buf.push(setContent.replace(/^\$\{([^}]*)}$/m, '$1')); | |
} | |
buf.push('; %>'); | |
} else { | |
let match = setContent.match(/^<%-\s*(.*?)\s*%>$/); | |
if (match) { | |
buf.push('_.escape(', match[1], '); %>'); | |
} else { | |
match = setContent.match(/^<%=?\s*(.*?)\s*%>$/); | |
if (match) { | |
buf.push(match[1], '; %>'); | |
} else { | |
buf.push('"', setContent.replace(/\n/g, '\\n').replace(/\r/g, '\\r'), '"; %>'); | |
} | |
} | |
} | |
} | |
}, | |
'c:out': { | |
open: (buf, res, attrs) => { | |
if (attrs.value.indexOf('${') === 0) { | |
buf.push('<%- ', unwrapElExpression(attrs.value), ' %>'); | |
} else { | |
buf.push(attrs.value); | |
} | |
} | |
}, | |
'form:form': { | |
open: (buf, res, attrs) => { | |
if (attrs.modelAttribute) { | |
currentModelAttribute = attrs.modelAttribute; | |
} | |
// handled later | |
buf.push(res[0]); | |
}, | |
close: (buf, res) => { | |
currentModelAttribute = null; | |
// handled later | |
buf.push(res[0]); | |
} | |
}, | |
'form:input': { | |
open: (buf, res, attrs) => { | |
if (attrs.path) { | |
let path = attrs.path; | |
if (currentModelAttribute) { | |
if (path.indexOf('${') === 0) { | |
path = currentModelAttribute + '[' + unwrapElExpression(path) + ']'; | |
} else { | |
path = currentModelAttribute + '.' + path; | |
} | |
} | |
let value = attrs.value; | |
if (value) { | |
buf.push(res[0]); | |
} else { | |
if (path.indexOf('${') === 0) { | |
value = ''; // not implemented, too complex | |
} else { | |
value = '${' + path + '}'; | |
} | |
buf.push(res[0].replace(/\bpath=/, 'value="' + value + '" path=')); | |
} | |
} | |
else { | |
// handled later | |
buf.push(res[0]); | |
} | |
}, | |
close: (buf, res) => { | |
// handled later | |
buf.push(res[0]); | |
} | |
}, | |
'form:radiobutton': { | |
open: (buf, res, attrs) => { | |
res[0] = res[0].replace('radiobutton', 'input type="radio"'); | |
STRATEGIES['form:input'].open(buf, res, attrs); | |
}, | |
close: (buf, res) => { | |
STRATEGIES['form:input'].close(buf, res); | |
} | |
}, | |
'jsp:include': { | |
open: (buf, res, attrs, selfClosing) => { | |
currentJspPageToInclude = attrs.page; | |
currentJspParams = {}; | |
if (selfClosing) { | |
buf.push(jspInclude()); | |
} | |
}, | |
close: buf => { | |
buf.push(jspInclude()); | |
} | |
}, | |
'jsp:param': { | |
mayReadContent: true, | |
open: (buf, res, attrs) => { | |
currentJspParams[attrs.name] = attrs.value; | |
} | |
}, | |
'spring:argument': { | |
mayReadContent: true, | |
open: (buf, res, attrs) => { | |
if (typeof attrs.value !== 'undefined') { | |
currentSpringArgs.push(attrs.value); | |
} | |
}, | |
close: (buf, res, argContent) => { | |
currentSpringArgs.push(argContent); | |
} | |
}, | |
'spring:message': (function () { | |
let messageCode; | |
let messageVar; | |
function writeResult(args, buf) { | |
args = args ? ',' + args : ''; | |
if (messageVar) { | |
if (messageCode.indexOf('${') !== -1) { | |
messageCode = unwrapElExpression(messageCode); | |
buf.push('<% var ', messageVar, ' = "i18n_" + ', messageCode, ' + "' + args + '"; %>'); | |
} else { | |
buf.push('<% var ', messageVar, ' = "i18n_', messageCode, args + '"; %>'); | |
} | |
} else { | |
if (messageCode.indexOf('${') !== -1) { | |
messageCode = unwrapElExpression(messageCode); | |
buf.push('<%= "i18n_" + ', messageCode, ' + "' + args + '" %>'); | |
} else { | |
buf.push('<%= "i18n_', messageCode, args + '" %>'); | |
} | |
} | |
} | |
return { | |
open: (buf, res, attrs, selfClosing) => { | |
messageCode = attrs.code; | |
messageVar = attrs.var; | |
currentSpringArgs = []; | |
if (selfClosing) { | |
writeResult(attrs.arguments, buf); | |
} | |
}, | |
close: buf => { | |
writeResult(currentSpringArgs.join(','), buf); | |
} | |
}; | |
})() | |
}; | |
while ((res = JSP_TAG.exec(jsp)) !== null) { | |
buf.push(jsp.substring(idx, res.index)); | |
const tag = res[0]; | |
const tagName = res[1]; | |
const closing = tag.indexOf('</') === 0; | |
if (!closing) { | |
const selfClosing = tag.indexOf('/>') === tag.length - 2; | |
const attrs = readAttributes(tag); | |
const strategy = STRATEGIES[tagName] || STRATEGIES.DEFAULT; | |
const open = strategy.open || STRATEGIES.DEFAULT.open; | |
open(buf, res, attrs, selfClosing); | |
if (strategy.mayReadContent && !selfClosing) { | |
bufBackup = buf; | |
buf = []; | |
} | |
} | |
else { | |
const strategy = STRATEGIES[tagName] || STRATEGIES.DEFAULT; | |
const close = strategy.close || STRATEGIES.DEFAULT.close; | |
if (bufBackup) { | |
const content = buf.join(''); | |
buf = bufBackup; | |
bufBackup = null; | |
close(buf, res, content); | |
} else { | |
close(buf, res); | |
} | |
} | |
idx = JSP_TAG.lastIndex; | |
} | |
buf.push(jsp.substring(idx)); | |
return buf.join(''); | |
} | |
function replaceIncludes(jsp) { | |
let res; | |
const buf = []; | |
let idx = 0; | |
while ((res = INCLUDE_DIRECTIVE.exec(jsp)) !== null) { | |
buf.push(jsp.substring(idx, res.index)); | |
buf.push(resolveInclude(res[1], attributes)); | |
idx = INCLUDE_DIRECTIVE.lastIndex; | |
} | |
buf.push(jsp.substring(idx)); | |
return buf.join(''); | |
} | |
function convert(jsp) { | |
jsp = replaceElOperators(jsp); | |
jsp = replaceJspTags(jsp); | |
jsp = replaceFormInputs(jsp); | |
jsp = jsp.replace(FORM, '$1'); | |
jsp = replaceIncludes(jsp); | |
return jsp.replace(JSP_DIRECTIVE_OR_COMMENT, ''); | |
} | |
return _.template(convert(jsp))(attributes) | |
.replace(/\n+\s*\n+/g, '\n'); | |
} | |
function resolveFile(jspFile, attributes, cfg) { | |
cfg = cfg || {}; | |
cfg.jspDir = cfg.jspRoot; | |
if (!cfg.jspDir) { | |
cfg.jspDir = path.dirname(jspFile); | |
cfg.jspRoot = cfg.jspDir; | |
} | |
cfg.jspFile = jspFile; | |
const jsp = fs.readFileSync(jspFile).toString(); | |
return resolve(jsp, attributes, cfg); | |
} | |
module.exports = { | |
resolve, | |
resolveFile | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%@ page contentType="text/html" %> | |
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> | |
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> | |
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> | |
<%-- some comment --%> | |
<c:choose> | |
<c:when test="${myVar != 'someVal'}"> | |
<p>should not be shown</p> | |
</c:when> | |
<c:when test="${myVar2 == 'someOtherVal'}"> | |
<p>should be shown 1</p> | |
</c:when> | |
<c:otherwise> | |
<p>should also not be shown</p> | |
</c:otherwise> | |
</c:choose> | |
<c:choose> | |
<c:when test="${myVar == 'someVal'}"> | |
<p>should be shown 2</p> | |
</c:when> | |
<c:when test="${myVar != 'someVal'}"> | |
<p>should not be shown</p> | |
</c:when> | |
<c:otherwise> | |
<p>should also not be shown</p> | |
</c:otherwise> | |
</c:choose> | |
<c:choose> | |
<c:when test="${false}"> | |
<p>should not be shown</p> | |
</c:when> | |
<c:otherwise> | |
<p>should be shown 3</p> | |
</c:otherwise> | |
</c:choose> | |
<form:form modelAttribute="formModel"> | |
<c:forEach items="${things}" var="thing"> | |
<form:input type="number" path="formModelMember" data-plop="${thing.name}"/> | |
<c:if test="${myVar == 'someVal'}"> | |
<input type="date" value="${bar}"> | |
</c:if> | |
<c:if test="${myVar2 != 'someVal'}"> | |
<input type="number" value="42"> | |
</c:if> | |
</c:forEach> | |
<c:out value="notavar"/> | |
<c:out value="${avar}"/> | |
<un:supported jsp="tag"></un:supported> | |
<a title="<spring:message code="message.code" javaScriptEscape="true"/>" href="#">Link</a> | |
<!-- @include sibling1: --> | |
<%@ include file="sibling1.jsp" %> | |
<c:set var="anewvar" value="${anoldvar}"/> | |
<p>anewvar: ${anewvar}</p> | |
<c:set var="multilineSet"> | |
foo | |
bar | |
</c:set> | |
<p>multilineSet: ${multilineSet}</p> | |
<c:set var="escapedSet"> | |
<c:out value="${avar}"/> | |
</c:set> | |
<p>escapedSet: ${escapedSet}</p> | |
<!-- jsp:include sibling1: --> | |
<jsp:include page="sibling1.jsp"/> | |
<c:if test="${empty emptyVar and empty emptyVar2}"> | |
<p>I support "empty"!</p> | |
</c:if> | |
<c:if test="${not empty nonEmptyVar}"> | |
<p>I support "not empty"!</p> | |
</c:if> | |
<p>I support "${false or true ? 'or' : 'error'}" too!</p> | |
<form:radiobutton path="${radioPath}" value="1" id="${radioPath}1"/> | |
<c:forEach begin="${one}" end="3" var="idx"> | |
<span>${idx}</span> | |
</c:forEach> | |
<p><spring:message code="message.with.args" arguments="arg1,arg2"/></p> | |
<c:set var="includedVar" value="INCLUDEDVAR"/> | |
<span><c:out value="foo${includedVar}"/></span> | |
<span><c:out value="${includedVar}bar"/></span> | |
<span><c:out value="foo${includedVar}bar"/></span> | |
</form:form> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<p>Text from sibling.jsp with some <c:out value="${bar}"/></p> | |
<c:set var="dynamicParam" value="dynamic, resolved"/> | |
<jsp:include page="sibling2.jsp"> | |
<jsp:param name="param1" value="static"/> | |
<jsp:param name="param2" value="${dynamicParam}"/> | |
</jsp:include> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<dl> | |
<dt>Param 1</dt><dd>${param.param1}</dd> | |
<dt>Param 2</dt><dd>${param.param2}</dd> | |
</dl> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment