11const path = require ( 'path' ) ;
2- const { escapeSequences } = require ( '../../Utils' ) ;
2+ const { stringifyData } = require ( '../../Utils' ) ;
33const { loadModule } = require ( '../../../Common/FileUtils' ) ;
4- const { yellow } = require ( 'ansis' ) ;
54
6- const compileModeWarning = ( ) => {
7- // TODO: warning
8- console . log (
9- yellow `[html-bundler-webpack-plugin] WARNING: Eta supports only rendering to an html string and cannot compile into a JS template function.${ '\n' } You can use ESJ for using the JS template function with variables.`
10- ) ;
11- } ;
5+ const includeRegexp = / = i n c l u d e \( (?< file > .+ ?) (?: \) | , \s * { (?< data > .+ ?) } \) ) / g;
126
137const preprocessor = ( loaderContext , options ) => {
148 const Eta = loadModule ( 'eta' , ( ) => require ( 'eta' ) . Eta ) ;
@@ -22,7 +16,7 @@ const preprocessor = (loaderContext, options) => {
2216 }
2317
2418 const eta = new Eta ( {
25- useWith : true , // allow using variables in template without `it.` scope
19+ useWith : true , // allow using variables in template without `it.` namespace
2620 ...options ,
2721 views, // directory that contains templates
2822 } ) ;
@@ -36,54 +30,67 @@ const preprocessor = (loaderContext, options) => {
3630 * Render template into HTML.
3731 * Called for rendering of template defined as entry point.
3832 *
39- * @param {string } template
33+ * @param {string } source The template source code.
4034 * @param {string } resourcePath
4135 * @param {{} } data
4236 * @return {string }
4337 */
4438 render : async
45- ? ( template , { resourcePath, data = { } } ) => {
46- return eta . renderStringAsync ( template , data ) ;
39+ ? ( source , { resourcePath, data = { } } ) => {
40+ return eta . renderStringAsync ( source , data ) ;
4741 }
48- : ( template , { resourcePath, data = { } } ) => {
49- return eta . renderString ( template , data ) ;
42+ : ( source , { resourcePath, data = { } } ) => {
43+ return eta . renderString ( source , data ) ;
5044 } ,
5145
5246 /**
5347 * Compile template into template function.
5448 * Called when a template is loaded in JS in `compile` mode.
5549 *
56- * Note:
57- * Eta does not compile the template into template function source code,
58- * so we compile the template into an HTML string that will be wrapped in a function for client-side compatibility.
59- *
60- * @param {string } template
50+ * @param {string } source The template source code.
6151 * @param {string } resourcePath
6252 * @param {{} } data
6353 * @return {string }
6454 */
65- compile : async
66- ? ( template , { resourcePath, data = { } } ) => {
67- compileModeWarning ( ) ;
68- return eta . renderStringAsync ( template , data ) ;
69- }
70- : ( template , { resourcePath, data = { } } ) => {
71- compileModeWarning ( ) ;
72- return eta . renderString ( template , data ) ;
73- } ,
55+ compile : ( source , { resourcePath, data = { } } ) => {
56+ const varName = options . varName || 'it' ;
57+ const eta = new Eta ( {
58+ useWith : true , // allow using variables in template without `it.` namespace
59+ ...options ,
60+ views,
61+ } ) ;
62+
63+ // parse and replace the partial file and data
64+ // include("./file.eta") => require("./file.eta")({...it, ...{}})
65+ // include('./file.eta', { name: 'eta' }) => require('./file.eta')({...it, ...{name: 'eta'}})
66+ const templateFunctionBody = eta
67+ . compileToString ( source )
68+ . replaceAll ( includeRegexp , `=require($<file>)({...${ varName } , ...{$<data>}})` ) ;
69+
70+ return `function(${ varName } ){${ templateFunctionBody } }` ;
71+ } ,
7472
7573 /**
7674 * Export the compiled template function contained resolved source asset files.
7775 * Note: this method is required for `compile` mode.
7876 *
79- * @param {string } content The source code of the template function.
77+ * @param {string } templateFunction The source code of the template function.
78+ * @param {{} } data The object with variables passed in template.
8079 * @return {string } The exported template function.
8180 */
82- export : ( content ) => {
83- const fnName = 'templateFn' ;
81+ export : ( templateFunction , { data } ) => {
82+ // note: resolved the file is for node, therefore, we need to get the module path plus file for browser
83+ const runtimeFile = path . join ( path . dirname ( require . resolve ( 'eta' ) ) , 'browser.module.mjs' ) ;
84+ const exportFunction = 'templateFn' ;
8485 const exportCode = 'module.exports=' ;
8586
86- return `const ${ fnName } = () => '` + escapeSequences ( content ) + `';${ exportCode } ${ fnName } ;` ;
87+ return `
88+ var { Eta } = require('${ runtimeFile } ');
89+ var eta = new Eta(${ stringifyData ( options ) } );
90+ var __data__ = ${ stringifyData ( data ) } ;
91+ var etaFn = ${ templateFunction } ;
92+ var ${ exportFunction } = (context) => etaFn.bind(eta)(Object.assign(__data__, context));
93+ ${ exportCode } ${ exportFunction } ;` ;
8794 } ,
8895 } ;
8996} ;
0 commit comments