From bbdc562abc121f3d51ec95ed64bc84d62b4f13f3 Mon Sep 17 00:00:00 2001 From: Ruskin Constant Date: Thu, 3 Sep 2020 15:27:52 +0100 Subject: [PATCH] feat: transpile .ts imports --- handlers/importHandler.js | 32 +++-- package-lock.json | 5 + package.json | 1 + test/mocks/importts/GET.mock | 4 + test/mocks/importts/script.ts | 5 + test/mockserver.js | 238 +++++++++++++++++----------------- 6 files changed, 156 insertions(+), 129 deletions(-) create mode 100644 test/mocks/importts/GET.mock create mode 100644 test/mocks/importts/script.ts diff --git a/handlers/importHandler.js b/handlers/importHandler.js index ff33ae4..2537ca3 100644 --- a/handlers/importHandler.js +++ b/handlers/importHandler.js @@ -1,18 +1,24 @@ const fs = require('fs'); const path = require('path'); +const ts = require('typescript'); module.exports = function importHandler(value, context, request) { - if (!/^#import/m.test(value)) return value; + if (!/^#import/m.test(value)) return value; - return value - .replace(/^#import (.*);/m, function (includeStatement, file) { - const importThisFile = file.replace(/['"]/g, ''); - const content = fs.readFileSync(path.join(context, importThisFile)); - if (importThisFile.endsWith('.js')) { - return JSON.stringify(eval(content.toString())); - } else { - return content; - } - }) - .replace(/\r\n?/g, '\n'); -} + return value + .replace(/^#import (.*);/m, function (includeStatement, file) { + const importThisFile = file.replace(/['"]/g, ''); + const content = fs.readFileSync(path.join(context, importThisFile)); + if (importThisFile.endsWith('.ts')) { + const { outputText } = ts.transpileModule(content.toString(), { + compilerOptions: { module: ts.ModuleKind.CommonJS }, + }); + return JSON.stringify(eval(outputText)); + } else if (importThisFile.endsWith('.js')) { + return JSON.stringify(eval(content.toString())); + } else { + return content; + } + }) + .replace(/\r\n?/g, '\n'); +}; diff --git a/package-lock.json b/package-lock.json index 636c644..ae1694b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1577,6 +1577,11 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, + "typescript": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz", + "integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==" + }, "uglify-js": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", diff --git a/package.json b/package.json index ba9004e..e46e0d1 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "colors": "^1.3.2", "header-case-normalizer": "^1.0.3", "js-combinatorics": "^0.5.0", + "typescript": "^4.0.2", "yargs": "^12.0.1" }, "devDependencies": { diff --git a/test/mocks/importts/GET.mock b/test/mocks/importts/GET.mock new file mode 100644 index 0000000..4ef86de --- /dev/null +++ b/test/mocks/importts/GET.mock @@ -0,0 +1,4 @@ +HTTP/1.1 200 OK +Content-Type: application/json + +#import './script.ts'; \ No newline at end of file diff --git a/test/mocks/importts/script.ts b/test/mocks/importts/script.ts new file mode 100644 index 0000000..a1240c4 --- /dev/null +++ b/test/mocks/importts/script.ts @@ -0,0 +1,5 @@ +const dateResponse = { + date: new Date(), +}; + +export default dateResponse; diff --git a/test/mockserver.js b/test/mockserver.js index 4ff6870..8f76b86 100644 --- a/test/mockserver.js +++ b/test/mockserver.js @@ -23,14 +23,14 @@ function processRequest(url, method) { * Processes request within custom ENV */ function processRequestEnv(url, method, envs) { - let cleanupEnv = function() {}; + let cleanupEnv = function () {}; for (let name in envs) { if (envs.hasOwnProperty(name)) { process.env[name] = envs[name]; - cleanupEnv = (function(name, next) { - return function() { + cleanupEnv = (function (name, next) { + return function () { delete process.env[name]; next(); }; @@ -43,19 +43,19 @@ function processRequestEnv(url, method, envs) { cleanupEnv(); } -describe('mockserver', function() { - beforeEach(function() { +describe('mockserver', function () { + beforeEach(function () { mockserver.headers = []; res = { headers: null, status: null, body: null, - writeHead: function(status, headers) { + writeHead: function (status, headers) { this.status = status; this.headers = headers; }, - end: function(body) { + end: function (body) { this.body = body; }, }; @@ -64,7 +64,7 @@ describe('mockserver', function() { url: null, method: null, headers: [], - on: function(event, cb) { + on: function (event, cb) { if (event === 'end') { cb(); } @@ -72,8 +72,8 @@ describe('mockserver', function() { }; }); - describe('mockserver()', function() { - it('should return a valid response', function() { + describe('mockserver()', function () { + it('should return a valid response', function () { processRequest('/test', 'GET'); assert.equal(res.body, 'Welcome!'); @@ -81,14 +81,14 @@ describe('mockserver', function() { assert.equal(JSON.stringify(res.headers), '{"Content-Type":"text"}'); }); - it('should return 404 if the mock does not exist', function() { + it('should return 404 if the mock does not exist', function () { processRequest('/not-there', 'GET'); assert.equal(res.status, 404); assert.equal(res.body, 'Not Mocked'); }); - it('should be able to handle trailing slashes without changing the name of the mockfile', function() { + it('should be able to handle trailing slashes without changing the name of the mockfile', function () { processRequest('/test/', 'GET'); assert.equal(res.status, 200); @@ -96,7 +96,7 @@ describe('mockserver', function() { assert.equal(JSON.stringify(res.headers), '{"Content-Type":"text"}'); }); - it('should be able to handle multiple headers', function() { + it('should be able to handle multiple headers', function () { processRequest('/multiple-headers/', 'GET'); assert.equal(res.status, 200); @@ -106,56 +106,56 @@ describe('mockserver', function() { ); }); - it('should combine the identical headers names', function() { + it('should combine the identical headers names', function () { processRequest('/multiple-headers-same-name/', 'GET'); - + assert.equal(res.headers['Set-Cookie'].length, 3); - }) + }); - it('should be able to handle status codes different than 200', function() { + it('should be able to handle status codes different than 200', function () { processRequest('/return-204', 'GET'); assert.equal(res.status, 204); }); - it('should be able to handle HTTP methods other than GET', function() { + it('should be able to handle HTTP methods other than GET', function () { processRequest('/return-200', 'POST'); assert.equal(res.status, 200); }); - it('should be able to handle empty bodies', function() { + it('should be able to handle empty bodies', function () { processRequest('/return-empty-body', 'GET'); assert.equal(res.status, 204); assert.equal(res.body, ''); }); - it('should be able to correctly map /', function() { + it('should be able to correctly map /', function () { processRequest('/', 'GET'); assert.equal(res.body, 'homepage'); }); - it('should be able to map multi-level urls', function() { + it('should be able to map multi-level urls', function () { processRequest('/test1/test2', 'GET'); assert.equal(res.body, 'multi-level url'); }); - it('should be able to handle GET parameters', function() { + it('should be able to handle GET parameters', function () { processRequest('/test?a=b', 'GET'); assert.equal(res.status, 200); }); - it('should default to GET.mock if no matching parameter file is found', function() { + it('should default to GET.mock if no matching parameter file is found', function () { processRequest('/test?a=c', 'GET'); assert.equal(res.status, 200); }); - it('should be able track custom headers', function() { + it('should be able track custom headers', function () { mockserver.headers = ['authorization']; processRequest('/request-headers', 'GET'); @@ -173,7 +173,7 @@ describe('mockserver', function() { assert.equal(res.body, 'admin authorized'); }); - it('should attempt to fall back to a base method if a custom header is not found in a file', function() { + it('should attempt to fall back to a base method if a custom header is not found in a file', function () { mockserver.headers = ['authorization']; req.headers['authorization'] = 'invalid'; @@ -187,7 +187,7 @@ describe('mockserver', function() { assert.equal(res.body, 'Not Mocked'); }); - it('should look for alternate combinations of headers if a custom header is not found', function() { + it('should look for alternate combinations of headers if a custom header is not found', function () { mockserver.headers = ['authorization', 'x-foo']; req.headers['authorization'] = 12; @@ -217,7 +217,7 @@ describe('mockserver', function() { assert.equal(res.body, 'header x-foo only'); }); - it('should be able track custom headers with variation and query params', function() { + it('should be able track custom headers with variation and query params', function () { mockserver.headers = ['authorization', 'x-foo']; req.headers['authorization'] = 12; req.headers['x-foo'] = 'Bar'; @@ -226,7 +226,7 @@ describe('mockserver', function() { assert.equal(res.body, 'that is a long filename'); }); - it('should be able track custom string headers with variation and query params', function() { + it('should be able track custom string headers with variation and query params', function () { mockserver.headers = 'authorization,x-foo'; req.headers['authorization'] = 12; @@ -238,7 +238,7 @@ describe('mockserver', function() { assert.equal(res.body, 'that is a long filename'); }); - it('should be able track custom ENV headers with variation and query params', function() { + it('should be able track custom ENV headers with variation and query params', function () { req.headers['authorization'] = 12; req.headers['x-foo'] = 'Bar'; @@ -250,7 +250,7 @@ describe('mockserver', function() { assert.equal(res.body, 'that is a long filename'); }); - it('should keep line feeds (U+000A)', function() { + it('should keep line feeds (U+000A)', function () { processRequest('/keep-line-feeds', 'GET'); assert.equal( @@ -264,7 +264,7 @@ describe('mockserver', function() { ); }); - it('should be able to include POST bodies in the mock location', function(done) { + it('should be able to include POST bodies in the mock location', function (done) { const req = new MockReq({ method: 'POST', url: '/return-200', @@ -277,14 +277,14 @@ describe('mockserver', function() { mockserver(mocksDirectory, verbose)(req, res); - req.on('end', function() { + req.on('end', function () { assert.equal(res.body, 'Hella'); assert.equal(res.status, 200); done(); }); }); - it('Should default to POST.mock if no match for body is found', function(done) { + it('Should default to POST.mock if no match for body is found', function (done) { const req = new MockReq({ method: 'POST', url: '/return-200', @@ -297,13 +297,13 @@ describe('mockserver', function() { mockserver(mocksDirectory, verbose)(req, res); - req.on('end', function() { + req.on('end', function () { assert.equal(res.status, 200); done(); }); }); - it('Should return 404 when no default .mock files are found', function() { + it('Should return 404 when no default .mock files are found', function () { mockserver.headers = ['authorization']; req.headers['authorization'] = 12; processRequest('/return-200?a=c', 'GET'); @@ -311,21 +311,21 @@ describe('mockserver', function() { assert.equal(res.status, 404); }); - it('should be able to handle imports', function() { + it('should be able to handle imports', function () { processRequest('/import', 'GET'); assert.equal(res.status, 200); assert.equal(res.body, JSON.stringify({ foo: 'bar' }, null, 4)); }); - it('should be able to handle eval', function() { + it('should be able to handle eval', function () { processRequest('/eval', 'GET'); assert.equal(res.status, 200); assert.deepEqual(JSON.parse(res.body), { foo: 'bar' }); }); - it('should be able to handle imports with content around import', function() { + it('should be able to handle imports with content around import', function () { processRequest('/import?around=true', 'GET'); assert.equal(res.status, 200); @@ -335,13 +335,19 @@ describe('mockserver', function() { ); }); - it('should be able to handle imports with js scripts', function() { + it('should be able to handle imports with ts scripts', function () { + processRequest('/importts', 'GET'); + assert.equal(res.status, 200); + assert.ok(Date.parse(JSON.parse(res.body).date)); + }); + + it('should be able to handle imports with js scripts', function () { processRequest('/importjs', 'GET'); assert.equal(res.status, 200); assert.ok(Date.parse(JSON.parse(res.body).date)); }); - it('should be able to handle imports with js scripts varying responses according to the the request - 1', function(done) { + it('should be able to handle imports with js scripts varying responses according to the the request - 1', function (done) { var req = new MockReq({ method: 'POST', url: '/importjs', @@ -352,13 +358,13 @@ describe('mockserver', function() { mockserver(mocksDirectory, verbose)(req, res); - req.on('end', function() { + req.on('end', function () { assert.equal(JSON.parse(res.body).prop, 'bar'); done(); }); }); - it('should be able to handle imports with js scripts varying responses according to the the request - 2', function(done) { + it('should be able to handle imports with js scripts varying responses according to the the request - 2', function (done) { var req = new MockReq({ method: 'POST', url: '/importjs', @@ -369,81 +375,81 @@ describe('mockserver', function() { mockserver(mocksDirectory, verbose)(req, res); - req.on('end', function() { + req.on('end', function () { assert.equal(JSON.parse(res.body).prop, 'baz'); done(); }); }); - it('should be able to handle dynamic header values', function() { + it('should be able to handle dynamic header values', function () { processRequest('/dynamic-headers', 'GET'); assert.equal(res.status, 200); assert.ok(Date.parse(res.headers['X-Subject-Token'])); assert.equal(res.body, 'dynamic headers\n'); }); - describe('wildcard directories', function() { - it('wildcard matches directories named __ with numeric slug', function() { + describe('wildcard directories', function () { + it('wildcard matches directories named __ with numeric slug', function () { processRequest('/wildcard/123', 'GET'); assert.equal(res.status, 200); assert.equal(res.body, 'this always comes up\n'); }); - it('wildcard matches directories named __ with string slug', function() { + it('wildcard matches directories named __ with string slug', function () { processRequest('/wildcard/abc', 'GET'); assert.equal(res.status, 200); assert.equal(res.body, 'this always comes up\n'); }); - it('wildcard matches directories named foo/__/bar with numeric slug', function() { + it('wildcard matches directories named foo/__/bar with numeric slug', function () { processRequest('/wildcard-extended/123/foobar', 'GET'); assert.equal(res.status, 200); assert.equal(res.body, 'wildcards-extended'); }); - it('wildcard matches directories named foo/__/bar with string slug', function() { + it('wildcard matches directories named foo/__/bar with string slug', function () { processRequest('/wildcard-extended/abc/foobar', 'GET'); assert.equal(res.status, 200); assert.equal(res.body, 'wildcards-extended'); }); - it('wildcard matches directories named foo/__/bar/__/fizz', function() { + it('wildcard matches directories named foo/__/bar/__/fizz', function () { processRequest('/wildcard-extended/abc/foobar/def/fizzbuzz', 'GET'); assert.equal(res.status, 200); assert.equal(res.body, 'wildcards-extended-multiple'); }); - it('__ not used if more specific match exist', function() { + it('__ not used if more specific match exist', function () { processRequest('/wildcard/exact', 'GET'); assert.equal(res.status, 200); assert.equal(res.body, 'more specific\n'); }); - it('should not resolve with missing slug', function() { + it('should not resolve with missing slug', function () { processRequest('/wildcard/', 'GET'); assert.equal(res.status, 404); }); }); - describe('.getResponseDelay', function() { - it('should return a value greater than zero when valid', function() { + describe('.getResponseDelay', function () { + it('should return a value greater than zero when valid', function () { const ownValueHeaders = [ { 'Response-Delay': 1 }, { 'Response-Delay': 99 }, { 'Response-Delay': '9999' }, ]; - ownValueHeaders.forEach(function(header) { + ownValueHeaders.forEach(function (header) { const val = mockserver.getResponseDelay(header); assert(val > 0, `Value found was ${val}`); }); }); - it('should return zero for invalid values', function() { + it('should return zero for invalid values', function () { const zeroValueHeaders = [ { 'Response-Delay': 'a' }, { 'Response-Delay': '' }, @@ -454,76 +460,76 @@ describe('mockserver', function() { null, undefined, ]; - zeroValueHeaders.forEach(function(header) { + zeroValueHeaders.forEach(function (header) { const val = mockserver.getResponseDelay(header); assert.equal(val, 0, `Value found was ${val}`); }); }); }); - describe('Custom response codes', function() { - it('response status codes depends on request case 400 Bad request', function (done) { - var req = new MockReq({ - method: 'POST', - url: '/headerimportjs', - headers: {}, - }); - req.write(JSON.stringify({ baz: '123' })); - req.end(); - - mockserver(mocksDirectory, verbose)(req, res); - - req.on('end', function () { - assert.equal(res.status, '400'); - done(); - }); + describe('Custom response codes', function () { + it('response status codes depends on request case 400 Bad request', function (done) { + var req = new MockReq({ + method: 'POST', + url: '/headerimportjs', + headers: {}, + }); + req.write(JSON.stringify({ baz: '123' })); + req.end(); + + mockserver(mocksDirectory, verbose)(req, res); + + req.on('end', function () { + assert.equal(res.status, '400'); + done(); + }); }); it('response status codes depends on request case 200 OK', function (done) { - var req = new MockReq({ - method: 'POST', - url: '/headerimportjs', - headers: {}, - }); - req.write(JSON.stringify({ foo: '123' })); - req.end(); - - mockserver(mocksDirectory, verbose)(req, res); - - req.on('end', function () { - assert.equal(res.status, '200'); - done(); - }); + var req = new MockReq({ + method: 'POST', + url: '/headerimportjs', + headers: {}, + }); + req.write(JSON.stringify({ foo: '123' })); + req.end(); + + mockserver(mocksDirectory, verbose)(req, res); + + req.on('end', function () { + assert.equal(res.status, '200'); + done(); + }); }); - }) + }); }); }); -describe('Monad methods', function() { - let m; - function fn(val) { - return { - ...val, - b: 2 - }; - } - const testData = { a: 1 }; - const expectData = { a: 1, b: 2 }; - beforeEach(function() { - m = Monad.of(testData); - }); +describe('Monad methods', function () { + let m; + function fn(val) { + return { + ...val, + b: 2, + }; + } + const testData = { a: 1 }; + const expectData = { a: 1, b: 2 }; + beforeEach(function () { + m = Monad.of(testData); + }); - it('Monad have static method `of`', function() { - assert.equal(typeof Monad.of, 'function'); - }); - it('Monad method `of` should return Object type Monad', function() { - assert.equal(m instanceof Monad, true); - }); - it('Monad method `map` should recive value and return Object type Monad', function() { - assert.equal(m.map(fn) instanceof Monad, true); - }); - it('Monad method `join` should return value', function () { - assert.strictEqual(m.join(), testData); - }); - it('Monad method `chain` should return value', function () { - assert.deepEqual(m.chain(fn), expectData); - }); + it('Monad have static method `of`', function () { + assert.equal(typeof Monad.of, 'function'); + }); + it('Monad method `of` should return Object type Monad', function () { + assert.equal(m instanceof Monad, true); + }); + it('Monad method `map` should recive value and return Object type Monad', function () { + assert.equal(m.map(fn) instanceof Monad, true); + }); + it('Monad method `join` should return value', function () { + assert.strictEqual(m.join(), testData); + }); + it('Monad method `chain` should return value', function () { + assert.deepEqual(m.chain(fn), expectData); + }); });