Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(compiler): add ability to specify template loader #173

Merged
merged 1 commit into from
May 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>