src/services/cli/generators/targetHTML.js
const path = require('path');
const fs = require('fs-extra');
const { provider } = require('jimple');
const CLISubCommand = require('../../../abstracts/cliSubCommand');
/**
* This is a CLI generator that allows the user to create an HTML file for a browser target.
* What it does is to force projext to create the default HTML file it would create if the target
* didn't have one and then it moves it to the target directory.
* @extends {CLISubCommand}
*/
class TargetHTMLGenerator extends CLISubCommand {
/**
* Class constructor.
* @param {Logger} appLogger To inform the user when the file has been generated, or if
* something went wrong.
* @param {Prompt} appPrompt To ask the user for the arget name and the file path.
* @param {Targets} targets To get the selected target information.
* @param {TargetsHTML} targetsHTML To generate the HTML file.
*/
constructor(appLogger, appPrompt, targets, targetsHTML) {
super();
/**
* A local reference for the `appLogger` service.
* @type {Logger}
*/
this.appLogger = appLogger;
/**
* A local reference for the `appPrompt` service.
* @type {Prompt}
*/
this.appPrompt = appPrompt;
/**
* A local reference for the `targets` service.
* @type {Targets}
*/
this.targets = targets;
/**
* A local reference for the `targetsHTML` service.
* @type {TargetsHTML}
*/
this.targetsHTML = targetsHTML;
/**
* The resource type the user will have to select on the CLI command that manages the
* generator.
* @type {string}
*/
this.name = 'html';
/**
* A short description of what the generator does.
* @type {string}
*/
this.description = 'Generate a browser target HTML template';
}
/**
* This method first prompts the user for information about the target and the file that is
* going to create, then uses the {@link TargetsHTML} to create a _"default HTML file"_, and it
* finally moves it to the selected path.
* @return {Promise<undefined,Error>}
*/
handle() {
// Get the _"default browser target"_.
const defaultTarget = this.targets.getDefaultTarget('browser');
// Define the prompt schema.
const schema = {
target: {
default: defaultTarget.name,
description: 'Target',
message: 'It should be the name of valid browser target',
required: true,
// Validate that the selected target exists and its type is `browser`.
conform: (value) => (
this.targets.targetExists(value) &&
this.targets.getTarget(value).is.browser
),
},
filename: {
default: defaultTarget.html.template,
// Validate the name of the HTML file.
pattern: /^[a-zA-Z0-9\.-_]+\.html$/i,
description: 'Filename',
message: 'It should be a valid name for an HTML file',
required: true,
},
overwrite: {
type: 'boolean',
default: 'yes',
description: 'Overwrite existing file',
required: true,
// Only ask for an overwrite confirmation if the file already exists.
ask: () => {
const target = this.targets.getTarget(this.appPrompt.getValue('target'));
const filename = this.appPrompt.getValue('filename');
return fs.pathExistsSync(path.join(target.paths.source, filename));
},
},
};
let filepath;
let moving = false;
// Ask the user...
return this.appPrompt.ask(schema)
.then((results) => {
// Get the selected target information.
const target = this.targets.getTarget(results.target);
// Build the HTML file absolute path.
filepath = path.join(target.paths.source, results.filename);
// Check if the file already exists.
const exists = fs.pathExistsSync(filepath);
let nextStep;
// If the file doesn't exist or if it exists but the user choose to overwrite it...
if (!exists || (exists && results.overwrite)) {
// Generate it on the temp directory.
const tempPath = this.targetsHTML.getFilepath(target, true);
moving = true;
// Move the HTML file from the temp directory to the selected path.
nextStep = fs.move(tempPath, filepath);
}
return nextStep;
})
.then(() => {
// If the file was successfully moved, inform the user.
if (moving) {
this.appLogger.success(`The HTML file was successfully generated: ${filepath}`);
}
})
.catch((error) => {
let result;
// If the process failed and it wasn't because the user canceled the input...
if (error.message !== 'canceled') {
// ...show the error.
this.appLogger.error('There was an error while generating the HTML file');
result = Promise.reject(error);
}
return result;
});
}
}
/**
* The service provider that once registered on the app container will set an instance of
* `TargetHTMLGenerator` as the `targetHTMLGenerator` service.
* @example
* // Register it on the container
* container.register(targetHTMLGenerator);
* // Getting access to the service instance
* const targetHTMLGenerator = container.get('targetHTMLGenerator');
* @type {Provider}
*/
const targetHTMLGenerator = provider((app) => {
app.set('targetHTMLGenerator', () => new TargetHTMLGenerator(
app.get('appLogger'),
app.get('appPrompt'),
app.get('targets'),
app.get('targetsHTML')
));
});
module.exports = {
TargetHTMLGenerator,
targetHTMLGenerator,
};