Skip to content

Commit

Permalink
Merge a2464bb into 08570c2
Browse files Browse the repository at this point in the history
  • Loading branch information
oshi97 committed Aug 24, 2021
2 parents 08570c2 + a2464bb commit 7eea877
Show file tree
Hide file tree
Showing 77 changed files with 1,670 additions and 270 deletions.
439 changes: 418 additions & 21 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions package.json
@@ -1,6 +1,6 @@
{
"name": "jambo",
"version": "1.11.0",
"version": "1.12.0",
"description": "A JAMStack implementation using Handlebars",
"main": "index.js",
"scripts": {
Expand Down Expand Up @@ -36,8 +36,9 @@
"i18next-conv": "^10.0.2",
"lodash": "^4.17.15",
"merge-options": "^2.0.0",
"npmlog": "^4.1.2",
"prompts": "^2.3.1",
"simple-git": "^1.131.0",
"simple-git": "^2.40.0",
"yargs": "^17.0.0"
},
"devDependencies": {
Expand All @@ -53,6 +54,8 @@
"src/**"
],
"verbose": true,
"globalSetup": "./tests/setup/globalsetup.js",
"globalTeardown": "./tests/setup/globalteardown.js",
"setupFilesAfterEnv": [
"./tests/setup/setup.js"
],
Expand Down
2 changes: 0 additions & 2 deletions sample_site/config.json

This file was deleted.

1 change: 0 additions & 1 deletion sample_site/config/global_config.json

This file was deleted.

1 change: 0 additions & 1 deletion sample_site/config/index.json

This file was deleted.

1 change: 0 additions & 1 deletion sample_site/overrides/placeholder.hbs

This file was deleted.

5 changes: 0 additions & 5 deletions sample_site/package-lock.json

This file was deleted.

11 changes: 0 additions & 11 deletions sample_site/package.json

This file was deleted.

1 change: 0 additions & 1 deletion sample_site/pages/index.html.hbs

This file was deleted.

Empty file.
9 changes: 0 additions & 9 deletions sample_site/theme/cream/standard/page.html.hbs

This file was deleted.

Empty file.
Empty file.
3 changes: 2 additions & 1 deletion src/buildJamboCLI.js
Expand Up @@ -4,6 +4,7 @@ const { parseJamboConfig } = require('./utils/jamboconfigutils');
const CommandRegistry = require('./commands/commandregistry');
const YargsFactory = require('./yargsfactory');
const CommandImporter = require('./commands/commandimporter');
const { error } = require('./utils/logger');

/**
* @param {string[]} argv the argv for the current process
Expand All @@ -14,7 +15,7 @@ module.exports = function buildJamboCLI(argv) {
const commandRegistry = new CommandRegistry();

if (argv.length < 3) {
console.error('You must provide Jambo with a command.');
error('You must provide Jambo with a command.');
return;
}

Expand Down
10 changes: 3 additions & 7 deletions src/cli.js
Expand Up @@ -3,12 +3,8 @@ const { exitWithError } = require('./utils/errorutils');
const buildJamboCLI = require('./buildJamboCLI');

// Exit with a non-zero exit code for unhandled rejections and uncaught exceptions
process.on('unhandledRejection', err => {
exitWithError(err);
});
process.on('uncaughtException', err => {
exitWithError(err);
});
process.on('unhandledRejection', exitWithError);
process.on('uncaughtException', exitWithError);

const jambo = buildJamboCLI(process.argv);
jambo && jambo.parse();
jambo && jambo.parseAsync().catch(exitWithError);
9 changes: 5 additions & 4 deletions src/commands/build/pagesetsbuilder.js
Expand Up @@ -6,6 +6,7 @@ const PageSet = require('../../models/pageset');
const PageTemplate = require('../../models/pagetemplate');
const PageTemplateDirector = require('./pagetemplatedirector');
const { NO_LOCALE } = require('../../constants');
const { warn } = require('../../utils/logger');

/**
* PageSetsBuilder is responsible for matching {@link PageConfigs} and
Expand Down Expand Up @@ -47,8 +48,8 @@ module.exports = class PageSetsBuilder {
const localeMessage = locale !== NO_LOCALE
? ` for '${locale}' locale`
: '';
console.log(
`Warning: No page templates found${localeMessage}, not generating a ` +
warn(
`No page templates found${localeMessage}, not generating a ` +
`page set${localeMessage}`);
continue;
}
Expand Down Expand Up @@ -80,8 +81,8 @@ module.exports = class PageSetsBuilder {
const localeMessage = config.getLocale() !== NO_LOCALE
? ` found for '${config.getLocale()}' locale`
: '';
console.log(
`Warning: No page template '${config.getPageName()}'${localeMessage}, ` +
warn(
`No page template '${config.getPageName()}'${localeMessage}, ` +
`not generating a '${config.getPageName()}' page${localeMessage}`);
continue;
}
Expand Down
22 changes: 20 additions & 2 deletions src/commands/build/pagewriter.js
Expand Up @@ -7,6 +7,8 @@ const UserError = require('../../errors/usererror');
const { NO_LOCALE } = require('../../constants');
const LocalizationConfig = require('../../models/localizationconfig');
const TemplateArgsBuilder = require('./templateargsbuilder');
const TemplateDataValidator = require('./templatedatavalidator');
const { info } = require('../../utils/logger');

/**
* PageWriter is responsible for writing output files for the given {@link PageSet} to
Expand All @@ -29,12 +31,20 @@ module.exports = class PageWriter {
*/
this._templateArgsBuilder =
new TemplateArgsBuilder(config.templateDataFormatterHook);

/**
* @type {TemplateDataValidator}
*/
this._templateDataValidator =
new TemplateDataValidator(config.templateDataValidationHook);
}

/**
* Writes a file to the output directory per page in the given PageSet.
*
* @param {PageSet} pageSet the collection of pages to generate
* @throws {UserError} on missing page config(s), validation hook execution
* failure, and invalid template data using Theme's validation hook
*/
writePages(pageSet) {
if (!pageSet || pageSet.getPages().length < 1) {
Expand All @@ -43,14 +53,13 @@ module.exports = class PageWriter {
const localeMessage = pageSet.getLocale() !== NO_LOCALE
? ` for '${pageSet.getLocale()}' locale`
: '';
console.log(`Writing files${localeMessage}`);
info(`Writing files${localeMessage}`);

for (const page of pageSet.getPages()) {
if (!page.getConfig()) {
throw new UserError(`Error: No config found for page: ${page.getName()}`);
}

console.log(`Writing output file for the '${page.getName()}' page`);
const templateArguments = this._templateArgsBuilder.buildArgs({
relativePath: this._calculateRelativePath(page.getOutputPath()),
pageName: page.getName(),
Expand All @@ -60,7 +69,16 @@ module.exports = class PageWriter {
locale: pageSet.getLocale(),
env: this._env
});

if(!this._templateDataValidator.validate({
pageName: page.getName(),
pageData: templateArguments,
partials: hbs.partials
})) {
throw new UserError('Invalid page template configuration(s).');
}

info(`Writing output file for the '${page.getName()}' page`);
const template = hbs.compile(page.getTemplateContents());
const outputHTML = template(templateArguments);

Expand Down
24 changes: 14 additions & 10 deletions src/commands/build/sitesgenerator.js
Expand Up @@ -21,6 +21,7 @@ const registerCustomHbsHelpers = require('../../handlebars/registercustomhbshelp
const SystemError = require('../../errors/systemerror');
const Translator = require('../../i18n/translator/translator');
const UserError = require('../../errors/usererror');
const { info } = require('../../utils/logger');

class SitesGenerator {
constructor(jamboConfig) {
Expand All @@ -44,9 +45,9 @@ class SitesGenerator {
// Pull all data from environment variables.
const envVarParser = EnvironmentVariableParser.create();
const env = envVarParser.parse(['JAMBO_INJECTED_DATA'].concat(jsonEnvVars));
console.log('Jambo Injected Data:', env['JAMBO_INJECTED_DATA']);
info('Jambo Injected Data:', env['JAMBO_INJECTED_DATA']);

console.log('Reading config files');
info('Reading config files');
const configNameToRawConfig = {};
fs.recurseSync(config.dirs.config, (path, relative, filename) => {
if (isValidFile(filename)) {
Expand Down Expand Up @@ -86,7 +87,7 @@ class SitesGenerator {
}
});

console.log('Reading partial files');
info('Reading partial files');
let partialRegistry;
try {
partialRegistry = PartialsRegistry.build({
Expand All @@ -111,28 +112,28 @@ class SitesGenerator {
new PageUniquenessValidator().validate(allPages);

// Clear the output directory but keep preserved files before writing new files
console.log('Cleaning output directory');
info('Cleaning output directory');
const shouldCleanOutput =
fs.existsSync(config.dirs.output) &&
!(this._isPreserved(config.dirs.output, config.dirs.preservedFiles));
if (shouldCleanOutput) {
this._clearDirectory(config.dirs.output, config.dirs.preservedFiles);
}

console.log('Creating static output directory');
info('Creating static output directory');
let staticDirs = [
`${config.dirs.themes}/${config.defaultTheme}/static`,
'static'
];
this._createStaticOutput(staticDirs, config.dirs.output);

console.log('Extracting translations');
info('Extracting translations');
const locales = GENERATED_DATA.getLocales();
const translations =
await this._extractTranslations(locales, configRegistry.getLocalizationConfig());

// Register built-in Jambo Handlebars helpers.
console.log('Registering Jambo Handlebars helpers');
info('Registering Jambo Handlebars helpers');
try {
registerHbsHelpers(hbs);
} catch (err) {
Expand All @@ -144,7 +145,7 @@ class SitesGenerator {
path.resolve(config.dirs.themes, config.defaultTheme, 'hbshelpers')
if (fs.existsSync(customHbsHelpersDir)) {
try {
console.log('Registering custom Handlebars helpers from the default theme');
info('Registering custom Handlebars helpers from the default theme');
registerCustomHbsHelpers(hbs, customHbsHelpersDir);
} catch (err) {
throw new UserError('Failed to register custom handlebars helpers', err.stack);
Expand All @@ -159,7 +160,7 @@ class SitesGenerator {
.create(locale, GENERATED_DATA.getLocaleFallbacks(locale), translations);
const handlebarsPreprocessor = new HandlebarsPreprocessor(translator);

console.log(`Registering Handlebars partials for locale ${locale}`);
info(`Registering Handlebars partials for locale ${locale}`);
for (const partial of partialRegistry.getPartials()) {
hbs.registerPartial(
partial.getName(),
Expand All @@ -177,15 +178,18 @@ class SitesGenerator {

const templateDataFormatterHook = path.resolve(
config.dirs.themes, config.defaultTheme, 'hooks', 'templatedataformatter.js');
const templateDataValidationHook = path.resolve(
config.dirs.themes, config.defaultTheme, 'hooks', 'templatedatavalidator.js');
// Write pages
new PageWriter({
outputDirectory: config.dirs.output,
templateDataFormatterHook: templateDataFormatterHook,
templateDataValidationHook: templateDataValidationHook,
env: env,
}).writePages(pageSet);
}

console.log('Done.');
info('Done.');
}

/**
Expand Down
50 changes: 50 additions & 0 deletions src/commands/build/templatedatavalidator.js
@@ -0,0 +1,50 @@
const fs = require('file-system');
const UserError = require('../../errors/usererror');
const { info } = require('../../utils/logger');

/**
* TemplateDataValidator is reponsible for checking data supplied to a page
* using Theme's custom validation steps (if any).
*/
module.exports = class TemplateDataValidator {
constructor(templateDataValidationHook) {
/**
* The path to template data validation hook.
* @type {string}
*/
this._templateDataValidationHook = templateDataValidationHook;

/**
* Whether or not the file for template data validation hook exists
* @type {boolean}
*/
this._hasHook = fs.existsSync(this._templateDataValidationHook);
}

/**
* Execute validation hook's function if file exists
*
* @param {Object} page
* @param {string} page.pageName name of the current page
* @param {Object} page.pageData template arguments for the current page
* @param {Object<string, Function|string>} partials
* mapping of partial name to partial. Handlebars converts
* partials from strings to Functions when they are used.
* @throws {UserError} on failure to execute hook
* @returns {boolean} whether or not to throw an exception on bad template arguments
*/
validate({ pageName, pageData, partials}) {
if (!this._hasHook) {
return true;
}
try {
info(`Validating configuration for page "${pageName}".`);
const validatorFunction = require(this._templateDataValidationHook);
return validatorFunction(pageData, partials);
} catch (err) {
const msg =
`Error executing validation hook from ${this._templateDataValidationHook}: `;
throw new UserError(msg, err.stack);
}
}
}
9 changes: 4 additions & 5 deletions src/commands/commandimporter.js
@@ -1,5 +1,6 @@
const path = require('path');
const fs = require('fs-extra');
const { warn } = require('../utils/logger');

/**
* Imports all custom {@link Command}s within a Jambo repository.
Expand All @@ -22,26 +23,24 @@ class CommandImporter {
let commandDirectories = ['commands'];
this._themeDir && commandDirectories.unshift(path.join(this._themeDir, 'commands'));
commandDirectories = commandDirectories.filter(fs.existsSync);

let customCommands = [];
if (commandDirectories.length > 0) {
const mergedDirectory = this._mergeCommandDirectories(commandDirectories);
const currDirectory = process.cwd();
fs.readdirSync(mergedDirectory)
.map(directoryPath => path.resolve(mergedDirectory, directoryPath))
.map(directoryPath => path.resolve(currDirectory, mergedDirectory, directoryPath))
.filter(directoryPath => directoryPath.endsWith('.js'))
.filter(directoryPath => fs.lstatSync(directoryPath).isFile())
.forEach(filePath => {
const requiredModule = require(filePath);

const commandClass = this._isLegacyImport(requiredModule) ?
this._handleLegacyImport(requiredModule) :
requiredModule;

if (this._validateCustomCommand(commandClass)) {
customCommands.push(commandClass);
} else {
console.warn(
`Command in ${path.basename(filePath)} was not formatted properly`);
warn(`Command in ${path.basename(filePath)} was not formatted properly`);
}
});

Expand Down

0 comments on commit 7eea877

Please sign in to comment.