diff2html/scripts/hulk.ts

189 lines
5.8 KiB
TypeScript
Raw Normal View History

/*
* Copyright 2011 Twitter, Inc.
* Licensed 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 CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
2019-12-29 22:31:32 +00:00
import * as path from 'path';
import * as fs from 'fs';
2019-12-29 22:31:32 +00:00
import * as hogan from 'hogan.js';
import nopt from 'nopt';
import * as mkderp from 'mkdirp';
const options = nopt(
{
namespace: String,
outputdir: path,
variable: String,
wrapper: String,
version: true,
2019-12-29 22:31:32 +00:00
help: true,
},
{
2019-12-29 22:31:32 +00:00
n: ['--namespace'],
o: ['--outputdir'],
vn: ['--variable'],
w: ['--wrapper'],
h: ['--help'],
v: ['--version'],
},
);
2019-12-29 22:31:32 +00:00
const specials = ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\'];
const specialsRegExp = new RegExp('(\\' + specials.join('|\\') + ')', 'g');
function escape(text: string): string {
2019-12-29 22:31:32 +00:00
return text.replace(specialsRegExp, '\\$1');
}
function cyan(text: string): string {
2019-12-29 22:31:32 +00:00
return '\x1B[36m' + text + '\x1B[39m';
}
function extractFiles(files: string[]): string[] {
2019-10-12 21:45:49 +00:00
const usage = `${cyan(
2019-12-29 22:31:32 +00:00
'USAGE:',
2019-10-12 21:45:49 +00:00
)} hulk [--wrapper wrapper] [--outputdir outputdir] [--namespace namespace] [--variable variable] FILES
2019-12-29 22:31:32 +00:00
${cyan('OPTIONS:')} [-w, --wrapper] :: wraps the template (i.e. amd)
[-o, --outputdir] :: outputs the templates as individual files to a directory
[-n, --namespace] :: prepend string to template names
[-vn, --variable] :: variable name for non-amd wrapper
2019-12-29 22:31:32 +00:00
${cyan('EXAMPLE:')} hulk --wrapper amd ./templates/*.mustache
2019-12-29 22:31:32 +00:00
${cyan('NOTE:')} hulk supports the "*" wildcard and allows you to target specific extensions too
`;
if (options.version) {
2024-12-23 22:34:58 +00:00
// eslint-disable-next-line @typescript-eslint/no-require-imports
2019-12-29 22:31:32 +00:00
console.log(require('../package.json').version);
process.exit(0);
}
if (!files.length || options.help) {
console.log(usage);
process.exit(0);
}
2019-12-29 22:31:32 +00:00
return files
.map((fileGlob: string) => {
if (/\*/.test(fileGlob)) {
2019-12-29 22:31:32 +00:00
const [fileGlobPrefix, fileGlobSuffix] = fileGlob.split('*');
2019-12-29 22:31:32 +00:00
return fs.readdirSync(fileGlobPrefix || '.').reduce<string[]>((previousFiles, relativeFilePath) => {
const file = path.join(fileGlobPrefix, relativeFilePath);
if (new RegExp(`${escape(fileGlobSuffix)}$`).test(relativeFilePath) && fs.statSync(file).isFile()) {
previousFiles.push(file);
}
return previousFiles;
}, []);
} else if (fs.statSync(fileGlob).isFile()) {
return [fileGlob];
} else {
return [];
}
})
.reduce((previous, current) => previous.concat(current), []);
}
// Remove utf-8 byte order mark, http://en.wikipedia.org/wiki/Byte_order_mark
function removeByteOrderMark(text: string): string {
if (text.charCodeAt(0) === 0xfeff) {
return text.substring(1);
}
return text;
}
// Wrap templates
function wrap(file: string, name: string, openedFile: string): string {
const hoganTemplateString = `new Hogan.Template(${hogan.compile(openedFile, { asString: true })})`;
2019-12-29 22:31:32 +00:00
const objectName = options.variable || 'templates';
const objectAccessor = `${objectName}["${name}"]`;
const objectStmt = `${objectAccessor} = ${hoganTemplateString};`;
switch (options.wrapper) {
2019-12-29 22:31:32 +00:00
case 'amd':
return `define(${
2019-12-29 22:31:32 +00:00
!options.outputdir ? `"${path.join(path.dirname(file), name)}", ` : ''
}["hogan.js"], function(Hogan) { return ${hoganTemplateString}; });`;
2019-12-29 22:31:32 +00:00
case 'node':
// If we have a template per file the export will expose the template directly
return options.outputdir ? `global.${objectStmt};\nmodule.exports = ${objectAccessor};` : `global.${objectStmt}`;
2019-12-29 22:31:32 +00:00
case 'ts':
2019-10-12 21:45:49 +00:00
return `// @ts-ignore\n${objectStmt}`;
default:
return objectStmt;
}
}
function prepareOutput(content: string): string {
2019-12-29 22:31:32 +00:00
const variableName = options.variable || 'templates';
switch (options.wrapper) {
2019-12-29 22:31:32 +00:00
case 'amd':
return content;
2019-12-29 22:31:32 +00:00
case 'node':
2019-10-12 21:45:49 +00:00
return `(function() {
if (!!!global.${variableName}) global.${variableName} = {};
var Hogan = require("hogan.js");
${content}
2019-12-29 22:31:32 +00:00
${!options.outputdir ? `module.exports = global.${variableName};\n` : ''})();`;
2019-10-12 21:45:49 +00:00
2019-12-29 22:31:32 +00:00
case 'ts':
2019-10-12 21:45:49 +00:00
return `import * as Hogan from "hogan.js";
type CompiledTemplates = { [name: string]: Hogan.Template };
export const ${variableName}: CompiledTemplates = {};
${content}`;
default:
2019-12-29 22:31:32 +00:00
return 'if (!!!' + variableName + ') var ' + variableName + ' = {};\n' + content;
}
}
// Write the directory
if (options.outputdir) {
mkderp.sync(options.outputdir);
}
// Prepend namespace to template name
function namespace(name: string): string {
2019-12-29 22:31:32 +00:00
return (options.namespace || '') + name;
}
// Write a template foreach file that matches template extension
const templates = extractFiles(options.argv.remain)
.map(file => {
2019-12-29 22:31:32 +00:00
const timmedFileContents = fs.readFileSync(file, 'utf8').trim();
if (!timmedFileContents) return;
2019-12-29 22:31:32 +00:00
const name = namespace(path.basename(file).replace(/\..*$/, ''));
const cleanFileContents = wrap(file, name, removeByteOrderMark(timmedFileContents));
if (!options.outputdir) return cleanFileContents;
2019-12-29 22:31:32 +00:00
const fileExtension = options.wrapper === 'ts' ? 'ts' : 'js';
2019-10-12 21:45:49 +00:00
return fs.writeFileSync(path.join(options.outputdir, `${name}.${fileExtension}`), prepareOutput(cleanFileContents));
})
2019-12-29 22:31:32 +00:00
.filter(templateContents => typeof templateContents !== 'undefined');
// Output templates
if (!templates.length || options.outputdir) process.exit(0);
2019-12-29 22:31:32 +00:00
console.log(prepareOutput(templates.join('\n')));