- Added precompiled templates to improve runtime performance further

This commit is contained in:
Ferdi Koomen 2020-05-24 00:25:01 +02:00
parent bf04a83145
commit fd90f3f7a0
9 changed files with 77 additions and 99 deletions

View File

@ -1,3 +1,5 @@
'use strict';
module.exports = {
presets: [
'@babel/preset-env',

1
.gitignore vendored
View File

@ -9,6 +9,7 @@ junit.xml
.vscode
*.iml
dist
src/**/*.js
archive
coverage
test/result

View File

@ -1,6 +1,6 @@
{
"name": "openapi-typescript-codegen",
"version": "0.2.9",
"version": "0.3.0",
"description": "NodeJS library that generates Typescript or Javascript clients based on the OpenAPI specification.",
"author": "Ferdi Koomen",
"homepage": "https://github.com/ferdikoomen/openapi-typescript-codegen",
@ -41,17 +41,17 @@
"bin/index.js",
"dist/index.js",
"dist/**/*.js",
"src/templates/**/*.hbs",
"src/templates/**/*.js",
"src/templates/**/*.ts"
],
"scripts": {
"clean": "rimraf ./dist ./test/result ./coverage",
"build": "tsc",
"run": "tsc && node ./test/index.js",
"test": "tsc && jest",
"test:update": "tsc && jest --updateSnapshot",
"test:watch": "tsc && jest --watch",
"test:coverage": "tsc && jest --coverage",
"clean": "rimraf ./dist ./src/**/*.js ./test/result ./coverage",
"build": "tsc && node ./precompile.js",
"run": "npm run build && node ./test/index.js",
"test": "npm run build && jest",
"test:update": "npm run build && jest --updateSnapshot",
"test:watch": "npm run build && jest --watch",
"test:coverage": "npm run builds && jest --coverage",
"eslint": "eslint \"./src/**/*.ts\"",
"eslint:fix": "eslint \"./src/**/*.ts\" --fix",
"prettier": "prettier \"./src/**/*.ts\" --check",

32
precompile.js Normal file
View File

@ -0,0 +1,32 @@
'use strict';
const Handlebars = require('handlebars');
const glob = require('glob');
const fs = require('fs');
const os = require('os');
glob.sync('./src/templates/**/*.hbs').forEach(file => {
// Read handlebars template as string
const template = fs.readFileSync(file, 'utf8').toString().trim();
// Precompile template to spec file, according to Handlebars this spec
// should be readable by a client, however it does not contain an export.
const templateSpec = Handlebars.precompile(template, {
strict: true,
noEscape: true,
preventIndent: true,
knownHelpersOnly: true,
knownHelpers: {
equals: true,
notEquals: true,
},
});
// Wrap the spec with an export statement, so we can import this using require.
const module = `'use strict'${os.EOL}module.exports = ${templateSpec};`;
// Write javascript module, this is the file we will import in the generator.
// This is much faster because we dont need to compile templates on the fly,
// plus we can load the handlebars/runtime which is quite lightweight.
fs.writeFileSync(file.replace('.hbs', '.js'), module);
});

View File

@ -1,20 +0,0 @@
import * as fs from 'fs';
import { readHandlebarsTemplate } from './readHandlebarsTemplate';
jest.mock('fs');
const fsExistsSync = fs.existsSync as jest.MockedFunction<typeof fs.existsSync>;
const fsReadFileSync = fs.readFileSync as jest.MockedFunction<typeof fs.readFileSync>;
describe('readHandlebarsTemplate', () => {
it('should read the template', () => {
fsExistsSync.mockReturnValue(true);
fsReadFileSync.mockReturnValue('{{{message}}}');
const template = readHandlebarsTemplate('/');
expect(template).toBeDefined();
expect(template({ message: 'Hello World!' })).toEqual('Hello World!');
});
});

View File

@ -1,21 +1,11 @@
import * as fs from 'fs';
import * as Handlebars from 'handlebars';
import * as Handlebars from 'handlebars/runtime';
/**
* Read and compile the Handlebars template.
* @param filePath
*/
export function readHandlebarsTemplate(filePath: string): Handlebars.TemplateDelegate {
const template = fs.readFileSync(filePath, 'utf8').toString().trim();
const template = require(filePath);
return Handlebars.compile(template, {
strict: true,
noEscape: true,
preventIndent: true,
knownHelpersOnly: true,
knownHelpers: {
equals: true,
notEquals: true,
},
});
return Handlebars.template(template);
}

View File

@ -1,27 +0,0 @@
import * as fs from 'fs';
import { readHandlebarsTemplates } from './readHandlebarsTemplates';
jest.mock('fs');
const fsExistsSync = fs.existsSync as jest.MockedFunction<typeof fs.existsSync>;
const fsReadFileSync = fs.readFileSync as jest.MockedFunction<typeof fs.readFileSync>;
describe('readHandlebarsTemplates', () => {
it('should read the templates', () => {
fsExistsSync.mockReturnValue(true);
fsReadFileSync.mockReturnValue('{{{message}}}');
const template = readHandlebarsTemplates();
expect(template).toBeDefined();
expect(template.index).toBeDefined();
expect(template.model).toBeDefined();
expect(template.service).toBeDefined();
expect(template.settings).toBeDefined();
expect(template.index({ message: 'Hello World!' })).toEqual('Hello World!');
expect(template.model({ message: 'Hello World!' })).toEqual('Hello World!');
expect(template.service({ message: 'Hello World!' })).toEqual('Hello World!');
expect(template.settings({ message: 'Hello World!' })).toEqual('Hello World!');
});
});

View File

@ -1,4 +1,4 @@
import * as Handlebars from 'handlebars';
import * as Handlebars from 'handlebars/runtime';
import * as path from 'path';
import { readHandlebarsTemplate } from './readHandlebarsTemplate';
@ -24,41 +24,41 @@ export function readHandlebarsTemplates(): Templates {
registerHandlebarHelpers();
const templates: Templates = {
index: readHandlebarsTemplate(resolveTemplate('index.hbs')),
model: readHandlebarsTemplate(resolveTemplate('model.hbs')),
schema: readHandlebarsTemplate(resolveTemplate('schema.hbs')),
service: readHandlebarsTemplate(resolveTemplate('service.hbs')),
settings: readHandlebarsTemplate(resolveTemplate('core/OpenAPI.hbs')),
index: readHandlebarsTemplate(resolveTemplate('index.js')),
model: readHandlebarsTemplate(resolveTemplate('model.js')),
schema: readHandlebarsTemplate(resolveTemplate('schema.js')),
service: readHandlebarsTemplate(resolveTemplate('service.js')),
settings: readHandlebarsTemplate(resolveTemplate('core/OpenAPI.js')),
};
const partials = [
'exportEnum.hbs',
'exportInterface.hbs',
'exportType.hbs',
'extends.hbs',
'isNullable.hbs',
'isReadOnly.hbs',
'isRequired.hbs',
'parameters.hbs',
'result.hbs',
'schema.hbs',
'schemaArray.hbs',
'schemaDictionary.hbs',
'schemaEnum.hbs',
'schemaGeneric.hbs',
'schemaInterface.hbs',
'type.hbs',
'typeArray.hbs',
'typeDictionary.hbs',
'typeEnum.hbs',
'typeGeneric.hbs',
'typeInterface.hbs',
'typeReference.hbs',
'exportEnum.js',
'exportInterface.js',
'exportType.js',
'extends.js',
'isNullable.js',
'isReadOnly.js',
'isRequired.js',
'parameters.js',
'result.js',
'schema.js',
'schemaArray.js',
'schemaDictionary.js',
'schemaEnum.js',
'schemaGeneric.js',
'schemaInterface.js',
'type.js',
'typeArray.js',
'typeDictionary.js',
'typeEnum.js',
'typeGeneric.js',
'typeInterface.js',
'typeReference.js',
];
partials.forEach(partial => {
const templatePath = resolveTemplate(`partials/${partial}`);
const templateName = path.basename(partial, '.hbs');
const templateName = path.basename(partial, '.js');
const template = readHandlebarsTemplate(templatePath);
Handlebars.registerPartial(templateName, template);
});

View File

@ -1,4 +1,4 @@
import * as Handlebars from 'handlebars';
import * as Handlebars from 'handlebars/runtime';
export function registerHandlebarHelpers(): void {
Handlebars.registerHelper('equals', function (a: string, b: string, options: Handlebars.HelperOptions): string {