Skip to content

Commit

Permalink
feat(compiler): add ability to specify template loader
Browse files Browse the repository at this point in the history
fixes #172
  • Loading branch information
tripodsan committed May 4, 2020
1 parent 655f5db commit c5bdab7
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 11 deletions.
30 changes: 20 additions & 10 deletions src/compiler/Compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
* governing permissions and limitations under the License.
*/

/* eslint-disable no-await-in-loop */

// built-in modules
const path = require('path');
// declared dependencies
Expand All @@ -25,6 +27,7 @@ const VariableBinding = require('../parser/commands/VariableBinding');
const RuntimeCall = require('../parser/htl/nodes/RuntimeCall');
const Identifier = require('../parser/htl/nodes/Identifier');
const FunctionBlock = require('../parser/commands/FunctionBlock');
const TemplateLoader = require('./TemplateLoader.js');

const DEFAULT_TEMPLATE = 'JSCodeTemplate.js';
const RUNTIME_TEMPLATE = 'JSRuntimeTemplate.js';
Expand Down Expand Up @@ -59,14 +62,14 @@ module.exports = class Compiler {
this._sourceFile = null;
this._sourceOffset = 0;
this._moduleImportGenerator = Compiler.defaultModuleGenerator;
this._templateLoader = TemplateLoader('.');
}

/**
* @deprecated use {@link #withDirectoty()} instead.
*/
withOutputDirectory(dir) {
this._dir = dir;
return this;
return this.withDirectory(dir);
}

/**
Expand All @@ -76,6 +79,7 @@ module.exports = class Compiler {
*/
withDirectory(dir) {
this._dir = dir;
this._templateLoader = TemplateLoader(dir);
return this;
}

Expand Down Expand Up @@ -157,6 +161,15 @@ module.exports = class Compiler {
return this;
}

/**
* Sets the function that loads the templates.
* @param {function} fn the async function taking the baseDir and file name.
*/
withTemplateLoader(fn) {
this._templateLoader = fn;
return this;
}

/**
* Compiles the specified source file and saves the result, overwriting the
* file name.
Expand Down Expand Up @@ -252,14 +265,11 @@ module.exports = class Compiler {
const c = commands[i];
if (c instanceof TemplateReference) {
if (c.isTemplate()) {
let templatePath = path.resolve(baseDir, c.filename);
// eslint-disable-next-line no-await-in-loop
if (!(await fse.pathExists(templatePath))) {
templatePath = path.resolve(this._dir, c.filename);
}
// eslint-disable-next-line no-await-in-loop
const templateSource = await fse.readFile(templatePath, 'utf-8');
// eslint-disable-next-line no-await-in-loop
const {
data: templateSource,
path: templatePath,
} = await this._templateLoader(baseDir, c.filename);

const res = await this._parse(templateSource, path.dirname(templatePath), mods);

// add recursive templates, if any.
Expand Down
71 changes: 71 additions & 0 deletions src/compiler/TemplateLoader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
const path = require('path');
const fse = require('fs-extra');

/**
* Creates an template loader.
* @param {string[]} roots Root directories for resolution.
*/
module.exports = function createLoader(roots) {
if (!Array.isArray(roots)) {
// eslint-disable-next-line no-param-reassign
roots = [roots];
}

/**
* Resolves the template against the given roots.
* @param {string} baseDir additional root
* @param {string} uri template uri
* @returns {Promise<string>}
*/
async function resolve(baseDir, uri) {
let bases = [baseDir, ...roots];

// if uri starts with '.' or '..', only consider specified bases.
// otherwise also consider apps and libs.
if (!uri.startsWith('./') && !uri.startsWith('../')) {
bases = bases.reduce((prev, root) => {
prev.push(root);
prev.push(path.resolve(root, 'apps'));
prev.push(path.resolve(root, 'libs'));
return prev;
}, []);
}

// eslint-disable-next-line no-restricted-syntax
for (const base of bases) {
const templatePath = path.resolve(base, uri);
// eslint-disable-next-line no-await-in-loop
if (await fse.pathExists(templatePath)) {
return templatePath;
}
}
throw Error(`Unable to resolve template: ${uri}. Search Path: ${bases}`);
}

/**
* Load the template.
* @param {string} baseDir additional root
* @param {string} uri template uri
* @returns {Promise<>} the template source and resolved path
*/
async function load(baseDir, uri) {
const templatePath = await resolve(baseDir, uri);
return {
data: await fse.readFile(templatePath, 'utf-8'),
path: templatePath,
};
}

return load;
};
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@
*/

const Compiler = require('./compiler/Compiler.js');
const createTemplateLoader = require('./compiler/TemplateLoader.js');
const Runtime = require('./runtime/Runtime.js');
const resly = require('./runtime/resly.js');

module.exports = Object.freeze({
Compiler,
Runtime,
createTemplateLoader,
resly,
});
4 changes: 3 additions & 1 deletion test/compiler_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const { JSDOM } = require('jsdom');
const Runtime = require('../src/runtime/Runtime');
const fsResourceLoader = require('../src/runtime/fsResourceLoader');
const Compiler = require('../src/compiler/Compiler');
const TemplateLoader = require('../src/compiler/TemplateLoader.js');

function serializeDom(node) {
if (node.doctype) {
Expand Down Expand Up @@ -80,14 +81,15 @@ function runTests(specs, typ = '', runtimeFn = () => {}, resultFn = (ret) => ret
const sourceFile = path.resolve(__dirname, 'specs', filename);
const tests = readTests(sourceFile);
const outputDir = path.join(__dirname, 'generated');
const rootProject1 = path.join(__dirname, 'specs', 'template_spec', 'jcr_root');
try {
fs.mkdirSync(outputDir);
} catch (e) {
// ignore
}

const compiler = new Compiler()
.withDirectory(outputDir)
.withTemplateLoader(TemplateLoader([outputDir, rootProject1]))
.withRuntimeVar(Object.keys(payload))
.withSourceFile(sourceFile)
.withSourceMap(true);
Expand Down
8 changes: 8 additions & 0 deletions test/specs/template_spec.txt
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,12 @@ blah Hello, world.
<h1>This is the title</h1>
</div>
#
### template works on multiple roots
#
<div data-sly-use.lib="project1/template.htl" data-sly-call="${lib.tmpl}"></div>
===
<div>
<h1>Project This is the title</h1>
</div>
#
###
3 changes: 3 additions & 0 deletions test/specs/template_spec/jcr_root/apps/project1/template.htl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template data-sly-template.tmpl>
<h1>Project ${page.title}</h1>
</template>

0 comments on commit c5bdab7

Please sign in to comment.