From 0ddbe38ba45e642998e292188aa1edae4d0b56c7 Mon Sep 17 00:00:00 2001 From: Tom Z?hner Date: Sun, 1 Apr 2018 20:21:58 +0200 Subject: [PATCH 01/17] Implement config file parsing and schema based validation --- packages/typewiz-core/package.json | 1 + .../src/configuration-parser.spec.ts | 77 +++++++++++++++++++ .../typewiz-core/src/configuration-parser.ts | 32 ++++++++ packages/typewiz-core/src/typewiz.json | 50 ++++++++++++ yarn.lock | 19 ++++- 5 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 packages/typewiz-core/src/configuration-parser.spec.ts create mode 100644 packages/typewiz-core/src/configuration-parser.ts create mode 100644 packages/typewiz-core/src/typewiz.json diff --git a/packages/typewiz-core/package.json b/packages/typewiz-core/package.json index 0fef50e..1f8df99 100644 --- a/packages/typewiz-core/package.json +++ b/packages/typewiz-core/package.json @@ -13,6 +13,7 @@ }, "files": ["dist"], "dependencies": { + "ajv": "^6.4.0", "typescript": "^2.4.2" }, "engines": { diff --git a/packages/typewiz-core/src/configuration-parser.spec.ts b/packages/typewiz-core/src/configuration-parser.spec.ts new file mode 100644 index 0000000..370d408 --- /dev/null +++ b/packages/typewiz-core/src/configuration-parser.spec.ts @@ -0,0 +1,77 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +let typewizConfig = ''; +const mockFs = { + readFileSync: jest.fn((filePath: string, options?: any) => { + if (filePath === 'src/typewiz.json' || filePath === path.resolve('not-found-file.json')) { + return fs.readFileSync(filePath, options); + } else { + return typewizConfig; + } + }), +}; +jest.doMock('fs', () => mockFs); + +import { ConfigurationParser } from './configuration-parser'; + +describe('ConfigurationParser', () => { + it('should throw an error if given non-existing typewiz.json file', () => { + expect(() => { + const configParser = new ConfigurationParser(); + configParser.parse('not-found-file.json'); + }).toThrowError(`Could not find configuration file 'not-found-file.json'`); + }); + + it('should throw an error if given bad typewiz.json file', () => { + expect(() => { + typewizConfig = ''; + const configParser = new ConfigurationParser(); + configParser.parse('test/typewiz.json'); + }).toThrowError(`Could not parser configuration file: Unexpected token < in JSON at position 0`); + }); + + it('should throw an error if given a typewiz.json file with invalid properties', () => { + expect(() => { + typewizConfig = ` + { + "commond": { + "rootDir": "...", + "tsConfig": "..." + } + } + `; + const configParser = new ConfigurationParser(); + configParser.parse('test/typewiz.json'); + }).toThrowError(`typewiz.json should NOT have additional properties`); + }); + + it('should parse a valid typewiz.json file with no options set', () => { + typewizConfig = ` + { + } + `; + const configParser = new ConfigurationParser(); + configParser.parse('test/typewiz.json'); + }); + + it('should parse a valid typewiz.json file with all options set', () => { + typewizConfig = ` + { + "common": { + "rootDir": "...", + "tsConfig": "..." + }, + "instrument": { + "instrumentCallExpressions": true, + "instrumentImplicitThis": true + }, + "applyTypes": { + "prefix": "TypeWiz |" + } + } + `; + const configParser = new ConfigurationParser(); + configParser.parse('test/typewiz.json'); + }); +}); diff --git a/packages/typewiz-core/src/configuration-parser.ts b/packages/typewiz-core/src/configuration-parser.ts new file mode 100644 index 0000000..36b4dfa --- /dev/null +++ b/packages/typewiz-core/src/configuration-parser.ts @@ -0,0 +1,32 @@ +import * as Ajv from 'ajv'; +import * as fs from 'fs'; +import * as path from 'path'; + +export class ConfigurationParser { + private typewizConfig: any; + + public parse(configurationPath: string) { + const typewizConfigSchema = JSON.parse(fs.readFileSync('src/typewiz.json', { encoding: 'utf8' })); + + let typewizConfigString; + try { + typewizConfigString = fs.readFileSync(path.resolve(configurationPath), { encoding: 'utf8' }); + } catch (error) { + throw new Error("Could not find configuration file '" + configurationPath + "'"); + } + + let typewizConfig; + try { + typewizConfig = JSON.parse(typewizConfigString); + } catch (error) { + throw new Error('Could not parser configuration file: ' + error.message); + } + + const ajv = new Ajv(); + const valid = ajv.validate(typewizConfigSchema, typewizConfig); + if (!valid) { + throw new Error(ajv.errorsText(ajv.errors, { dataVar: 'typewiz.json' })); + } + this.typewizConfig = typewizConfig; + } +} diff --git a/packages/typewiz-core/src/typewiz.json b/packages/typewiz-core/src/typewiz.json new file mode 100644 index 0000000..213c2a6 --- /dev/null +++ b/packages/typewiz-core/src/typewiz.json @@ -0,0 +1,50 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Typewiz configuration schema", + "type": "object", + "properties": { + "common": { + "type": "object", + "properties": { + "rootDir": { + "type": "string", + "description": + "If given, all the file paths in the collected type info will be resolved relative to this directory." + }, + "tsConfig": { + "type": "string", + "description": "Path to your project's tsconfig file." + } + } + }, + "instrument": { + "type": "object", + "properties": { + "instrumentCallExpressions": { + "type": "boolean", + "default": false + }, + "instrumentImplicitThis": { + "type": "boolean", + "description": "Requires common.tsConfig to be set", + "default": false + }, + "skipTwizDeclarations": { + "type": "boolean", + "default": false + } + } + }, + "applyTypes": { + "type": "object", + "properties": { + "prefix": { + "type": "string", + "description": + "A prefix that will be added in front of each type applied. You can use a javascript comment to mark the automatically added types. The prefix will be added after the colon character, just before the actual type." + } + } + } + }, + "additionalProperties": false +} diff --git a/yarn.lock b/yarn.lock index 2f21724..4d972ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -139,6 +139,15 @@ ajv@^5.1.0: fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" +ajv@^6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.4.0.tgz#d3aff78e9277549771daf0164cff48482b754fc6" + dependencies: + fast-deep-equal "^1.0.0" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.3.0" + uri-js "^3.0.2" + align-text@^0.1.1, align-text@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" @@ -4170,10 +4179,6 @@ typescript@^2.4.2: version "2.6.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4" -typescript@^2.8.1: - version "2.8.1" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.8.1.tgz#6160e4f8f195d5ba81d4876f9c0cc1fbc0820624" - typewiz@^0.7.0, typewiz@^0.7.2: version "0.7.2" resolved "https://registry.yarnpkg.com/typewiz/-/typewiz-0.7.2.tgz#3b2d4f5f35989789c82ff952c194d4e18702fd07" @@ -4205,6 +4210,12 @@ unzip-response@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" +uri-js@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-3.0.2.tgz#f90b858507f81dea4dcfbb3c4c3dbfa2b557faaa" + dependencies: + punycode "^2.1.0" + url-parse-lax@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" From 871b84f4c889ba005f914f3897a93cc8e58199f7 Mon Sep 17 00:00:00 2001 From: Tom Z?hner Date: Sun, 1 Apr 2018 21:11:26 +0200 Subject: [PATCH 02/17] Don't throw an exception if the config file doesn't exist --- packages/typewiz-core/src/configuration-parser.spec.ts | 8 +++----- packages/typewiz-core/src/configuration-parser.ts | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/typewiz-core/src/configuration-parser.spec.ts b/packages/typewiz-core/src/configuration-parser.spec.ts index 370d408..d48234b 100644 --- a/packages/typewiz-core/src/configuration-parser.spec.ts +++ b/packages/typewiz-core/src/configuration-parser.spec.ts @@ -16,11 +16,9 @@ jest.doMock('fs', () => mockFs); import { ConfigurationParser } from './configuration-parser'; describe('ConfigurationParser', () => { - it('should throw an error if given non-existing typewiz.json file', () => { - expect(() => { - const configParser = new ConfigurationParser(); - configParser.parse('not-found-file.json'); - }).toThrowError(`Could not find configuration file 'not-found-file.json'`); + it('should handle a non-existing typewiz.json file by using an empty default config', () => { + const configParser = new ConfigurationParser(); + configParser.parse('not-found-file.json'); }); it('should throw an error if given bad typewiz.json file', () => { diff --git a/packages/typewiz-core/src/configuration-parser.ts b/packages/typewiz-core/src/configuration-parser.ts index 36b4dfa..63147dd 100644 --- a/packages/typewiz-core/src/configuration-parser.ts +++ b/packages/typewiz-core/src/configuration-parser.ts @@ -12,7 +12,7 @@ export class ConfigurationParser { try { typewizConfigString = fs.readFileSync(path.resolve(configurationPath), { encoding: 'utf8' }); } catch (error) { - throw new Error("Could not find configuration file '" + configurationPath + "'"); + typewizConfigString = '{}'; } let typewizConfig; From b4865ce779a8075201c9be5efac174b5799a3640 Mon Sep 17 00:00:00 2001 From: Tom Z?hner Date: Mon, 2 Apr 2018 15:45:07 +0200 Subject: [PATCH 03/17] Read files asynchronously --- .../src/configuration-parser.spec.ts | 56 ++++++++++--------- .../typewiz-core/src/configuration-parser.ts | 8 ++- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/packages/typewiz-core/src/configuration-parser.spec.ts b/packages/typewiz-core/src/configuration-parser.spec.ts index d48234b..08b83bd 100644 --- a/packages/typewiz-core/src/configuration-parser.spec.ts +++ b/packages/typewiz-core/src/configuration-parser.spec.ts @@ -3,35 +3,40 @@ import * as path from 'path'; let typewizConfig = ''; const mockFs = { - readFileSync: jest.fn((filePath: string, options?: any) => { - if (filePath === 'src/typewiz.json' || filePath === path.resolve('not-found-file.json')) { - return fs.readFileSync(filePath, options); - } else { - return typewizConfig; - } - }), + readFile: jest.fn( + (filePath: string, options: any, callback: (err: NodeJS.ErrnoException, data: string) => void) => { + if (filePath === 'src/typewiz.json' || filePath === path.resolve('not-found-file.json')) { + fs.readFile(filePath, options, callback); + } else { + callback(null, typewizConfig); + } + }, + ), }; jest.doMock('fs', () => mockFs); import { ConfigurationParser } from './configuration-parser'; describe('ConfigurationParser', () => { - it('should handle a non-existing typewiz.json file by using an empty default config', () => { + it('should handle a non-existing typewiz.json file by using an empty default config', async () => { const configParser = new ConfigurationParser(); - configParser.parse('not-found-file.json'); + await configParser.parse('not-found-file.json'); }); - it('should throw an error if given bad typewiz.json file', () => { - expect(() => { - typewizConfig = ''; - const configParser = new ConfigurationParser(); - configParser.parse('test/typewiz.json'); - }).toThrowError(`Could not parser configuration file: Unexpected token < in JSON at position 0`); + it('should throw an error if given bad typewiz.json file', async () => { + await expect( + (() => { + typewizConfig = ''; + const configParser = new ConfigurationParser(); + return configParser.parse('test/typewiz.json'); + })(), + ).rejects.toThrow(`Could not parser configuration file: Unexpected token < in JSON at position 0`); }); - it('should throw an error if given a typewiz.json file with invalid properties', () => { - expect(() => { - typewizConfig = ` + it('should throw an error if given a typewiz.json file with invalid properties', async () => { + await expect( + (() => { + typewizConfig = ` { "commond": { "rootDir": "...", @@ -39,21 +44,22 @@ describe('ConfigurationParser', () => { } } `; - const configParser = new ConfigurationParser(); - configParser.parse('test/typewiz.json'); - }).toThrowError(`typewiz.json should NOT have additional properties`); + const configParser = new ConfigurationParser(); + return configParser.parse('test/typewiz.json'); + })(), + ).rejects.toThrow(`typewiz.json should NOT have additional properties`); }); - it('should parse a valid typewiz.json file with no options set', () => { + it('should parse a valid typewiz.json file with no options set', async () => { typewizConfig = ` { } `; const configParser = new ConfigurationParser(); - configParser.parse('test/typewiz.json'); + await configParser.parse('test/typewiz.json'); }); - it('should parse a valid typewiz.json file with all options set', () => { + it('should parse a valid typewiz.json file with all options set', async () => { typewizConfig = ` { "common": { @@ -70,6 +76,6 @@ describe('ConfigurationParser', () => { } `; const configParser = new ConfigurationParser(); - configParser.parse('test/typewiz.json'); + await configParser.parse('test/typewiz.json'); }); }); diff --git a/packages/typewiz-core/src/configuration-parser.ts b/packages/typewiz-core/src/configuration-parser.ts index 63147dd..0987412 100644 --- a/packages/typewiz-core/src/configuration-parser.ts +++ b/packages/typewiz-core/src/configuration-parser.ts @@ -1,16 +1,18 @@ import * as Ajv from 'ajv'; import * as fs from 'fs'; import * as path from 'path'; +import * as util from 'util'; export class ConfigurationParser { private typewizConfig: any; - public parse(configurationPath: string) { - const typewizConfigSchema = JSON.parse(fs.readFileSync('src/typewiz.json', { encoding: 'utf8' })); + public async parse(configurationPath: string): Promise { + const readFileAsync = util.promisify(fs.readFile); + const typewizConfigSchema = JSON.parse(await readFileAsync('src/typewiz.json', { encoding: 'utf8' })); let typewizConfigString; try { - typewizConfigString = fs.readFileSync(path.resolve(configurationPath), { encoding: 'utf8' }); + typewizConfigString = await readFileAsync(path.resolve(configurationPath), { encoding: 'utf8' }); } catch (error) { typewizConfigString = '{}'; } From 3cc8631961938272fe4b4d5fc4fcef5687965c94 Mon Sep 17 00:00:00 2001 From: Tom Z?hner Date: Mon, 2 Apr 2018 16:35:08 +0200 Subject: [PATCH 04/17] Move util.promisify outside of the ConfigurationParser --- packages/typewiz-core/src/configuration-parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/typewiz-core/src/configuration-parser.ts b/packages/typewiz-core/src/configuration-parser.ts index 0987412..4a52800 100644 --- a/packages/typewiz-core/src/configuration-parser.ts +++ b/packages/typewiz-core/src/configuration-parser.ts @@ -2,12 +2,12 @@ import * as Ajv from 'ajv'; import * as fs from 'fs'; import * as path from 'path'; import * as util from 'util'; +const readFileAsync = util.promisify(fs.readFile); export class ConfigurationParser { private typewizConfig: any; public async parse(configurationPath: string): Promise { - const readFileAsync = util.promisify(fs.readFile); const typewizConfigSchema = JSON.parse(await readFileAsync('src/typewiz.json', { encoding: 'utf8' })); let typewizConfigString; From 16457ccb7fcaceb4b9b5df177804b1b06cb12e38 Mon Sep 17 00:00:00 2001 From: Tom Z?hner Date: Mon, 2 Apr 2018 17:24:12 +0200 Subject: [PATCH 05/17] Add getters for options --- .../src/configuration-parser.spec.ts | 117 +++++++++++++++++- .../typewiz-core/src/configuration-parser.ts | 15 +++ 2 files changed, 131 insertions(+), 1 deletion(-) diff --git a/packages/typewiz-core/src/configuration-parser.spec.ts b/packages/typewiz-core/src/configuration-parser.spec.ts index 08b83bd..604bb45 100644 --- a/packages/typewiz-core/src/configuration-parser.spec.ts +++ b/packages/typewiz-core/src/configuration-parser.spec.ts @@ -68,7 +68,8 @@ describe('ConfigurationParser', () => { }, "instrument": { "instrumentCallExpressions": true, - "instrumentImplicitThis": true + "instrumentImplicitThis": true, + "skipTwizDeclarations": true }, "applyTypes": { "prefix": "TypeWiz |" @@ -78,4 +79,118 @@ describe('ConfigurationParser', () => { const configParser = new ConfigurationParser(); await configParser.parse('test/typewiz.json'); }); + + it('should return an ICompilerConfig if no configuration is specified', async () => { + await expect( + (async () => { + typewizConfig = ` + { + } + `; + const configParser = new ConfigurationParser(); + await configParser.parse('test/typewiz.json'); + return configParser.getCompilerOptions(); + })(), + ).resolves.toEqual({}); + }); + + it('should return an ICompilerConfig if a configuration is specified', async () => { + await expect( + (async () => { + typewizConfig = ` + { + "common": { + "rootDir": "...", + "tsConfig": "..." + } + } + `; + const configParser = new ConfigurationParser(); + await configParser.parse('test/typewiz.json'); + return configParser.getCompilerOptions(); + })(), + ).resolves.toEqual({ rootDir: '...', tsConfig: '...' }); + }); + + it('should return IInstrumentOptions if no configuration is specified', async () => { + await expect( + (async () => { + typewizConfig = ` + { + } + `; + const configParser = new ConfigurationParser(); + await configParser.parse('test/typewiz.json'); + return configParser.getInstrumentOptions(); + })(), + ).resolves.toEqual({}); + }); + + it('should return IInstrumentOptions if a configuration is specified', async () => { + await expect( + (async () => { + typewizConfig = ` + { + "common": { + "rootDir": "...", + "tsConfig": "..." + }, + "instrument": { + "instrumentCallExpressions": true, + "instrumentImplicitThis": true, + "skipTwizDeclarations": true + } + } + `; + const configParser = new ConfigurationParser(); + await configParser.parse('test/typewiz.json'); + return configParser.getInstrumentOptions(); + })(), + ).resolves.toEqual({ + instrumentCallExpressions: true, + instrumentImplicitThis: true, + rootDir: '...', + skipTwizDeclarations: true, + tsConfig: '...', + }); + }); + + it('should return IApplyTypesOptions if no configuration is specified', async () => { + await expect( + (async () => { + typewizConfig = ` + { + } + `; + const configParser = new ConfigurationParser(); + await configParser.parse('test/typewiz.json'); + return configParser.getApplyTypesOptions(); + })(), + ).resolves.toEqual({}); + }); + + it('should return IApplyTypesOptions if a configuration is specified', async () => { + await expect( + (async () => { + typewizConfig = ` + { + "common": { + "rootDir": "...", + "tsConfig": "..." + }, + "applyTypes": { + "prefix": "TypeWiz |" + } + } + `; + const configParser = new ConfigurationParser(); + await configParser.parse('test/typewiz.json'); + return configParser.getApplyTypesOptions(); + })(), + ).resolves.toEqual({ + prefix: 'TypeWiz |', + rootDir: '...', + tsConfig: '...', + }); + }); }); diff --git a/packages/typewiz-core/src/configuration-parser.ts b/packages/typewiz-core/src/configuration-parser.ts index 4a52800..1e149af 100644 --- a/packages/typewiz-core/src/configuration-parser.ts +++ b/packages/typewiz-core/src/configuration-parser.ts @@ -2,6 +2,9 @@ import * as Ajv from 'ajv'; import * as fs from 'fs'; import * as path from 'path'; import * as util from 'util'; +import { IApplyTypesOptions } from './apply-types'; +import { ICompilerOptions } from './compiler-helper'; +import { IInstrumentOptions } from './instrument'; const readFileAsync = util.promisify(fs.readFile); export class ConfigurationParser { @@ -31,4 +34,16 @@ export class ConfigurationParser { } this.typewizConfig = typewizConfig; } + + public getCompilerOptions(): ICompilerOptions { + return { ...this.typewizConfig.common }; + } + + public getInstrumentOptions(): IInstrumentOptions { + return { ...this.getCompilerOptions(), ...this.typewizConfig.instrument }; + } + + public getApplyTypesOptions(): IApplyTypesOptions { + return { ...this.getCompilerOptions(), ...this.typewizConfig.applyTypes }; + } } From 4beffc9b2dc85833e53b31c9ab501504384f3186 Mon Sep 17 00:00:00 2001 From: Tom Z?hner Date: Mon, 2 Apr 2018 19:24:05 +0200 Subject: [PATCH 06/17] Prepare configuration parser for external use --- packages/typewiz-core/package.json | 2 +- packages/typewiz-core/src/configuration-parser.spec.ts | 2 +- packages/typewiz-core/src/configuration-parser.ts | 4 +++- packages/typewiz-core/src/index.ts | 1 + packages/typewiz-core/src/integration.spec.ts | 1 + 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/typewiz-core/package.json b/packages/typewiz-core/package.json index 1f8df99..1b6b81e 100644 --- a/packages/typewiz-core/package.json +++ b/packages/typewiz-core/package.json @@ -7,7 +7,7 @@ "author": "Uri Shaked ", "license": "MIT", "scripts": { - "build": "rimraf dist && tsc && copyfiles -u 1 src/type-collector-snippet.ts dist", + "build": "rimraf dist && tsc && copyfiles -u 1 src/type-collector-snippet.ts src/typewiz.json dist", "lint": "tslint -p .", "prepublish": "yarn build" }, diff --git a/packages/typewiz-core/src/configuration-parser.spec.ts b/packages/typewiz-core/src/configuration-parser.spec.ts index 604bb45..ce3badc 100644 --- a/packages/typewiz-core/src/configuration-parser.spec.ts +++ b/packages/typewiz-core/src/configuration-parser.spec.ts @@ -5,7 +5,7 @@ let typewizConfig = ''; const mockFs = { readFile: jest.fn( (filePath: string, options: any, callback: (err: NodeJS.ErrnoException, data: string) => void) => { - if (filePath === 'src/typewiz.json' || filePath === path.resolve('not-found-file.json')) { + if (filePath === path.resolve('src', 'typewiz.json') || filePath === path.resolve('not-found-file.json')) { fs.readFile(filePath, options, callback); } else { callback(null, typewizConfig); diff --git a/packages/typewiz-core/src/configuration-parser.ts b/packages/typewiz-core/src/configuration-parser.ts index 1e149af..64c825e 100644 --- a/packages/typewiz-core/src/configuration-parser.ts +++ b/packages/typewiz-core/src/configuration-parser.ts @@ -11,7 +11,9 @@ export class ConfigurationParser { private typewizConfig: any; public async parse(configurationPath: string): Promise { - const typewizConfigSchema = JSON.parse(await readFileAsync('src/typewiz.json', { encoding: 'utf8' })); + const typewizConfigSchema = JSON.parse( + await readFileAsync(path.join(__dirname, 'typewiz.json'), { encoding: 'utf8' }), + ); let typewizConfigString; try { diff --git a/packages/typewiz-core/src/index.ts b/packages/typewiz-core/src/index.ts index 768f666..d0196f6 100644 --- a/packages/typewiz-core/src/index.ts +++ b/packages/typewiz-core/src/index.ts @@ -1,4 +1,5 @@ export * from './apply-types'; +export * from './configuration-parser'; export * from './instrument'; export * from './node-register'; export * from './type-collector'; diff --git a/packages/typewiz-core/src/integration.spec.ts b/packages/typewiz-core/src/integration.spec.ts index 2d7c31f..253e12f 100644 --- a/packages/typewiz-core/src/integration.spec.ts +++ b/packages/typewiz-core/src/integration.spec.ts @@ -5,6 +5,7 @@ import * as vm from 'vm'; import { transpileSource, virtualCompilerHost } from './test-utils/transpile'; const mockFs = { + readFile: jest.fn(fs.readFile), readFileSync: jest.fn(fs.readFileSync), writeFileSync: jest.fn(fs.writeFileSync), }; From f333b8736f146f2ee646fc2e59c97f368fc31618 Mon Sep 17 00:00:00 2001 From: Tom Z?hner Date: Mon, 2 Apr 2018 19:26:03 +0200 Subject: [PATCH 07/17] Use typewiz config in typewiz-webpack loader --- packages/typewiz-webpack/package.json | 4 +++- packages/typewiz-webpack/src/loader.ts | 19 +++++++++++---- yarn.lock | 33 +++++++++++++++++++++++--- 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/packages/typewiz-webpack/package.json b/packages/typewiz-webpack/package.json index 4819806..a0a3449 100644 --- a/packages/typewiz-webpack/package.json +++ b/packages/typewiz-webpack/package.json @@ -16,11 +16,13 @@ "node": ">= 6.4.0" }, "dependencies": { - "typewiz": "^0.7.0", + "loader-utils": "^1.1.0", + "typewiz-core": "^0.7.0", "webpack-sources": "^1.1.0" }, "devDependencies": { "@types/express": "^4.11.0", + "@types/loader-utils": "^1.1.3", "@types/webpack": "^3.8.3", "@types/webpack-sources": "^0.1.4", "rimraf": "^2.6.2" diff --git a/packages/typewiz-webpack/src/loader.ts b/packages/typewiz-webpack/src/loader.ts index 0a8cb33..00e7480 100644 --- a/packages/typewiz-webpack/src/loader.ts +++ b/packages/typewiz-webpack/src/loader.ts @@ -1,10 +1,21 @@ -import { instrument } from 'typewiz'; +import { getOptions } from 'loader-utils'; +import * as path from 'path'; +import { ConfigurationParser, instrument } from 'typewiz-core'; import { loader } from 'webpack'; -export function typewizLoader(this: loader.LoaderContext, source: string | null) { +export async function typewizLoader(this: loader.LoaderContext, source: string | undefined) { + const callback = this.async(); + + const typewizConfigPath = path.resolve(getOptions(this).typewizConfig || 'typewiz.json'); + this.addDependency(typewizConfigPath); + + const configurationParser = new ConfigurationParser(); + await configurationParser.parse(typewizConfigPath); + const filename = this.resourcePath; if (source && (filename.endsWith('.ts') || filename.endsWith('.tsx'))) { - return instrument(source, filename); + callback!(null, instrument(source, filename, configurationParser.getInstrumentOptions())); + } else { + callback!(null, source); } - return source; } diff --git a/yarn.lock b/yarn.lock index 4d972ca..e0dc251 100644 --- a/yarn.lock +++ b/yarn.lock @@ -40,6 +40,13 @@ version "22.0.1" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-22.0.1.tgz#6370a6d60cce3845e4cd5d00bf65f654264685bc" +"@types/loader-utils@^1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@types/loader-utils/-/loader-utils-1.1.3.tgz#82b9163f2ead596c68a8c03e450fbd6e089df401" + dependencies: + "@types/node" "*" + "@types/webpack" "*" + "@types/mime@*": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.0.tgz#5a7306e367c539b9f6543499de8dd519fac37a8b" @@ -85,7 +92,7 @@ "@types/source-list-map" "*" source-map "^0.6.1" -"@types/webpack@^3.8.3": +"@types/webpack@*", "@types/webpack@^3.8.3": version "3.8.11" resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-3.8.11.tgz#df2d7f8db43dbc15b4e8ecbdc91e6f68ed6b83ab" dependencies: @@ -492,6 +499,10 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +big.js@^3.1.3: + version "3.2.0" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" + binary-extensions@^1.0.0: version "1.11.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" @@ -1173,6 +1184,10 @@ elegant-spinner@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" +emojis-list@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + error-ex@^1.2.0, error-ex@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" @@ -2429,7 +2444,7 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" -json5@^0.5.1: +json5@^0.5.0, json5@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" @@ -2642,6 +2657,14 @@ load-json-file@^4.0.0: pify "^3.0.0" strip-bom "^3.0.0" +loader-utils@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" + dependencies: + big.js "^3.1.3" + emojis-list "^2.0.0" + json5 "^0.5.0" + locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -4179,7 +4202,11 @@ typescript@^2.4.2: version "2.6.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4" -typewiz@^0.7.0, typewiz@^0.7.2: +typescript@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.8.1.tgz#6160e4f8f195d5ba81d4876f9c0cc1fbc0820624" + +typewiz@^0.7.2: version "0.7.2" resolved "https://registry.yarnpkg.com/typewiz/-/typewiz-0.7.2.tgz#3b2d4f5f35989789c82ff952c194d4e18702fd07" dependencies: From 26b0ac76ced5a49ff40387b7ec860c3056b79cf6 Mon Sep 17 00:00:00 2001 From: Tom Z?hner Date: Tue, 3 Apr 2018 09:43:02 +0200 Subject: [PATCH 08/17] Only parse configuration once in loader --- packages/typewiz-webpack/src/loader.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/typewiz-webpack/src/loader.ts b/packages/typewiz-webpack/src/loader.ts index 00e7480..6e6e767 100644 --- a/packages/typewiz-webpack/src/loader.ts +++ b/packages/typewiz-webpack/src/loader.ts @@ -3,14 +3,23 @@ import * as path from 'path'; import { ConfigurationParser, instrument } from 'typewiz-core'; import { loader } from 'webpack'; +let promise: Promise; +let configurationParser: ConfigurationParser; + export async function typewizLoader(this: loader.LoaderContext, source: string | undefined) { const callback = this.async(); const typewizConfigPath = path.resolve(getOptions(this).typewizConfig || 'typewiz.json'); this.addDependency(typewizConfigPath); - const configurationParser = new ConfigurationParser(); - await configurationParser.parse(typewizConfigPath); + if (promise) { + await promise; + } + if (!configurationParser) { + configurationParser = new ConfigurationParser(); + promise = configurationParser.parse(typewizConfigPath); + await promise; + } const filename = this.resourcePath; if (source && (filename.endsWith('.ts') || filename.endsWith('.tsx'))) { From 7c636de92b4ff58ad1dc4541b8062a3c37bf721a Mon Sep 17 00:00:00 2001 From: Tom Z?hner Date: Tue, 3 Apr 2018 13:34:45 +0200 Subject: [PATCH 09/17] Implement configuration support for typewiz-node --- packages/typewiz-core/src/index.ts | 1 - packages/typewiz-node/README.md | 2 +- packages/typewiz-node/package.json | 2 +- packages/typewiz-node/src/index.ts | 20 ++++++++++++++----- .../src/node-register.ts | 20 +++++++++++++------ packages/typewiz-node/src/typewiz-node.ts | 13 +++++++++--- yarn.lock | 6 ------ 7 files changed, 41 insertions(+), 23 deletions(-) rename packages/{typewiz-core => typewiz-node}/src/node-register.ts (58%) diff --git a/packages/typewiz-core/src/index.ts b/packages/typewiz-core/src/index.ts index d0196f6..959ac84 100644 --- a/packages/typewiz-core/src/index.ts +++ b/packages/typewiz-core/src/index.ts @@ -1,5 +1,4 @@ export * from './apply-types'; export * from './configuration-parser'; export * from './instrument'; -export * from './node-register'; export * from './type-collector'; diff --git a/packages/typewiz-node/README.md b/packages/typewiz-node/README.md index 7bbdbfe..fc2405f 100644 --- a/packages/typewiz-node/README.md +++ b/packages/typewiz-node/README.md @@ -15,7 +15,7 @@ or ## Usage - typewiz-node + typewiz-node If you want to enable typewiz for your mocha test, simply add `-r typewiz-node/dist/register` to your mocha command-line, e.g.: diff --git a/packages/typewiz-node/package.json b/packages/typewiz-node/package.json index d321ebf..0b0ad33 100644 --- a/packages/typewiz-node/package.json +++ b/packages/typewiz-node/package.json @@ -20,7 +20,7 @@ }, "dependencies": { "ts-node": "^5.0.1", - "typewiz": "^0.7.2" + "typewiz-core": "^0.7.2" }, "devDependencies": { "rimraf": "^2.6.2" diff --git a/packages/typewiz-node/src/index.ts b/packages/typewiz-node/src/index.ts index f35fdd3..3fcf36a 100644 --- a/packages/typewiz-node/src/index.ts +++ b/packages/typewiz-node/src/index.ts @@ -1,8 +1,11 @@ +import * as path from 'path'; import * as tsnode from 'ts-node'; -import * as typewiz from 'typewiz'; -import { $_$twiz } from 'typewiz/dist/type-collector-snippet'; +import * as typewiz from 'typewiz-core'; +import { $_$twiz } from 'typewiz-core/dist/type-collector-snippet'; +import * as nodeRegister from './node-register'; export interface IOptions { + typewizConfig?: string; applyOnExit?: boolean; tsNode?: tsnode.Options; } @@ -11,10 +14,17 @@ export function getCollectedTypes() { return $_$twiz.get(); } -export function register(options: IOptions = {}) { - typewiz.register(); +export async function register(options: IOptions = {}) { + const typewizConfigPath = path.resolve(options && options.typewizConfig ? options.typewizConfig : 'typewiz.json'); + + const configurationParser = new typewiz.ConfigurationParser(); + await configurationParser.parse(typewizConfigPath); + + await nodeRegister.register(); tsnode.register(options.tsNode); if (options.applyOnExit !== false) { - process.on('exit', () => typewiz.applyTypes(getCollectedTypes())); + process.on('exit', () => { + typewiz.applyTypes(getCollectedTypes(), configurationParser.getApplyTypesOptions()); + }); } } diff --git a/packages/typewiz-core/src/node-register.ts b/packages/typewiz-node/src/node-register.ts similarity index 58% rename from packages/typewiz-core/src/node-register.ts rename to packages/typewiz-node/src/node-register.ts index 62f055a..468eb54 100644 --- a/packages/typewiz-core/src/node-register.ts +++ b/packages/typewiz-node/src/node-register.ts @@ -1,31 +1,39 @@ -import { instrument } from './index'; -import { $_$twiz } from './type-collector-snippet'; +import * as path from 'path'; +import { ConfigurationParser, instrument } from 'typewiz-core'; +import { $_$twiz } from 'typewiz-core/dist/type-collector-snippet'; type ICompileFunction = (source: string, filename: string) => void; export interface IRegisterOptions { - extensions: string[]; + typewizConfig?: string; + extensions?: string[]; } -export function register(options?: IRegisterOptions) { +export async function register(options?: IRegisterOptions) { options = Object.assign( { extensions: ['.ts', '.tsx'], + typewizConfig: 'typewiz.json', }, options, ); + const typewizConfigPath = path.resolve(options.typewizConfig!); + + const configurationParser = new ConfigurationParser(); + await configurationParser.parse(typewizConfigPath); + (global as any).$_$twiz = $_$twiz; const oldHooks: { [key: string]: any } = {}; - for (const extension of options.extensions) { + for (const extension of options.extensions!) { const oldHook = require.extensions[extension] || require.extensions['.js']; oldHooks[extension] = oldHook; require.extensions[extension] = (m: NodeModule & { _compile: ICompileFunction }, file) => { const oldCompile = m._compile; m._compile = (source: string, filename: string) => { m._compile = oldCompile; - m._compile(instrument(source, filename), filename); + m._compile(instrument(source, filename, configurationParser.getInstrumentOptions()), filename); }; oldHook(m, file); }; diff --git a/packages/typewiz-node/src/typewiz-node.ts b/packages/typewiz-node/src/typewiz-node.ts index 77bffd6..5a6b044 100644 --- a/packages/typewiz-node/src/typewiz-node.ts +++ b/packages/typewiz-node/src/typewiz-node.ts @@ -2,7 +2,14 @@ import { register } from './index'; -register(); +(async () => { + if (process.argv.length > 3) { + await register({ typewizConfig: process.argv[2] }); + process.argv.splice(2, 1); + } else { + await register(); + } -// Delegate the rest of the fun to ts-node: -require('ts-node/dist/_bin'); // tslint:disable-line:no-var-requires + // Delegate the rest of the fun to ts-node: + require('ts-node/dist/_bin'); // tslint:disable-line:no-var-requires +})(); diff --git a/yarn.lock b/yarn.lock index e0dc251..ac8953f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4206,12 +4206,6 @@ typescript@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.8.1.tgz#6160e4f8f195d5ba81d4876f9c0cc1fbc0820624" -typewiz@^0.7.2: - version "0.7.2" - resolved "https://registry.yarnpkg.com/typewiz/-/typewiz-0.7.2.tgz#3b2d4f5f35989789c82ff952c194d4e18702fd07" - dependencies: - typescript "^2.4.2" - uglify-js@^2.6: version "2.8.29" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" From 74b460d72084e6d299b217c72010440e596ac935 Mon Sep 17 00:00:00 2001 From: zoehneto Date: Wed, 4 Apr 2018 09:57:47 +0200 Subject: [PATCH 10/17] Mono repo fixes --- packages/typewiz-core/src/configuration-parser.spec.ts | 5 ++++- packages/typewiz-webpack/src/plugin.ts | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/typewiz-core/src/configuration-parser.spec.ts b/packages/typewiz-core/src/configuration-parser.spec.ts index ce3badc..db89496 100644 --- a/packages/typewiz-core/src/configuration-parser.spec.ts +++ b/packages/typewiz-core/src/configuration-parser.spec.ts @@ -5,7 +5,10 @@ let typewizConfig = ''; const mockFs = { readFile: jest.fn( (filePath: string, options: any, callback: (err: NodeJS.ErrnoException, data: string) => void) => { - if (filePath === path.resolve('src', 'typewiz.json') || filePath === path.resolve('not-found-file.json')) { + if ( + filePath === path.resolve('packages', 'typewiz-core', 'src', 'typewiz.json') || + filePath === path.resolve('not-found-file.json') + ) { fs.readFile(filePath, options, callback); } else { callback(null, typewizConfig); diff --git a/packages/typewiz-webpack/src/plugin.ts b/packages/typewiz-webpack/src/plugin.ts index 50fd545..b86620b 100644 --- a/packages/typewiz-webpack/src/plugin.ts +++ b/packages/typewiz-webpack/src/plugin.ts @@ -1,4 +1,4 @@ -import { getTypeCollectorSnippet } from 'typewiz'; +import { getTypeCollectorSnippet } from 'typewiz-core'; import { Compiler } from 'webpack'; import { ConcatSource } from 'webpack-sources'; From be344ed14160299de9a0d7b4b087e70ead5befd7 Mon Sep 17 00:00:00 2001 From: zoehneto Date: Wed, 4 Apr 2018 10:22:33 +0200 Subject: [PATCH 11/17] Require node 8 for typewiz-core, remove node 6.4.0 from ci, support node 6.4.0 in typewiz-node and typewiz-webpack through a polyfill --- .travis.yml | 1 - package.json | 2 +- packages/typewiz-core/package.json | 2 +- packages/typewiz-node/package.json | 3 ++- packages/typewiz-node/src/index.ts | 1 + packages/typewiz-webpack/package.json | 1 + packages/typewiz-webpack/src/index.ts | 1 + 7 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 910a83f..50cd4c1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: node_js sudo: false node_js: - - 6.4.0 - 8 - stable script: diff --git a/package.json b/package.json index 5c7f503..dfc7f88 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "typescript": "^2.8.1" }, "engines": { - "node": ">= 6.4.0", + "node": ">= 8.0.0", "yarn": ">= 1.3.2" }, "jest": { diff --git a/packages/typewiz-core/package.json b/packages/typewiz-core/package.json index 1b6b81e..de11f90 100644 --- a/packages/typewiz-core/package.json +++ b/packages/typewiz-core/package.json @@ -17,6 +17,6 @@ "typescript": "^2.4.2" }, "engines": { - "node": ">= 6.4.0" + "node": ">= 8.0.0" } } diff --git a/packages/typewiz-node/package.json b/packages/typewiz-node/package.json index 0b0ad33..00a5796 100644 --- a/packages/typewiz-node/package.json +++ b/packages/typewiz-node/package.json @@ -20,7 +20,8 @@ }, "dependencies": { "ts-node": "^5.0.1", - "typewiz-core": "^0.7.2" + "typewiz-core": "^0.7.2", + "util.promisify": "^1.0.0" }, "devDependencies": { "rimraf": "^2.6.2" diff --git a/packages/typewiz-node/src/index.ts b/packages/typewiz-node/src/index.ts index 3fcf36a..a48bd55 100644 --- a/packages/typewiz-node/src/index.ts +++ b/packages/typewiz-node/src/index.ts @@ -1,3 +1,4 @@ +require('util.promisify/shim')(); // tslint:disable-line:no-var-requires import * as path from 'path'; import * as tsnode from 'ts-node'; import * as typewiz from 'typewiz-core'; diff --git a/packages/typewiz-webpack/package.json b/packages/typewiz-webpack/package.json index a0a3449..1f1b956 100644 --- a/packages/typewiz-webpack/package.json +++ b/packages/typewiz-webpack/package.json @@ -18,6 +18,7 @@ "dependencies": { "loader-utils": "^1.1.0", "typewiz-core": "^0.7.0", + "util.promisify": "^1.0.0", "webpack-sources": "^1.1.0" }, "devDependencies": { diff --git a/packages/typewiz-webpack/src/index.ts b/packages/typewiz-webpack/src/index.ts index 10f76b7..e72bd1f 100644 --- a/packages/typewiz-webpack/src/index.ts +++ b/packages/typewiz-webpack/src/index.ts @@ -1,3 +1,4 @@ +require('util.promisify/shim')(); // tslint:disable-line:no-var-requires export { typewizLoader as default } from './loader'; export { TypewizPlugin } from './plugin'; export { typewizCollectorMiddleware } from './middleware'; From 55ff9431c0a103f9344bb1d2e27bd91b0fe5693d Mon Sep 17 00:00:00 2001 From: Tom Z?hner Date: Sun, 8 Apr 2018 13:52:34 +0200 Subject: [PATCH 12/17] Remove typewiz.json argument from typewiz-node --- packages/typewiz-node/README.md | 2 +- packages/typewiz-node/src/typewiz-node.ts | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/typewiz-node/README.md b/packages/typewiz-node/README.md index fc2405f..7bbdbfe 100644 --- a/packages/typewiz-node/README.md +++ b/packages/typewiz-node/README.md @@ -15,7 +15,7 @@ or ## Usage - typewiz-node + typewiz-node If you want to enable typewiz for your mocha test, simply add `-r typewiz-node/dist/register` to your mocha command-line, e.g.: diff --git a/packages/typewiz-node/src/typewiz-node.ts b/packages/typewiz-node/src/typewiz-node.ts index 5a6b044..1287c5b 100644 --- a/packages/typewiz-node/src/typewiz-node.ts +++ b/packages/typewiz-node/src/typewiz-node.ts @@ -3,12 +3,7 @@ import { register } from './index'; (async () => { - if (process.argv.length > 3) { - await register({ typewizConfig: process.argv[2] }); - process.argv.splice(2, 1); - } else { - await register(); - } + await register(); // Delegate the rest of the fun to ts-node: require('ts-node/dist/_bin'); // tslint:disable-line:no-var-requires From df42286909f864d4ae24a4b5303113e69435aa07 Mon Sep 17 00:00:00 2001 From: Tom Z?hner Date: Sun, 8 Apr 2018 14:01:53 +0200 Subject: [PATCH 13/17] Implement configuration finder --- packages/typewiz-core/src/configuration-parser.ts | 5 +++++ packages/typewiz-node/src/index.ts | 8 +++++--- packages/typewiz-node/src/node-register.ts | 9 +++------ packages/typewiz-webpack/src/loader.ts | 7 ++++--- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/packages/typewiz-core/src/configuration-parser.ts b/packages/typewiz-core/src/configuration-parser.ts index 64c825e..c547bf1 100644 --- a/packages/typewiz-core/src/configuration-parser.ts +++ b/packages/typewiz-core/src/configuration-parser.ts @@ -1,6 +1,7 @@ import * as Ajv from 'ajv'; import * as fs from 'fs'; import * as path from 'path'; +import * as ts from 'typescript'; import * as util from 'util'; import { IApplyTypesOptions } from './apply-types'; import { ICompilerOptions } from './compiler-helper'; @@ -10,6 +11,10 @@ const readFileAsync = util.promisify(fs.readFile); export class ConfigurationParser { private typewizConfig: any; + public findConfigFile(cwd: string): string { + return ts.findConfigFile(cwd, ts.sys.fileExists, 'typewiz.json'); + } + public async parse(configurationPath: string): Promise { const typewizConfigSchema = JSON.parse( await readFileAsync(path.join(__dirname, 'typewiz.json'), { encoding: 'utf8' }), diff --git a/packages/typewiz-node/src/index.ts b/packages/typewiz-node/src/index.ts index a48bd55..8164a81 100644 --- a/packages/typewiz-node/src/index.ts +++ b/packages/typewiz-node/src/index.ts @@ -16,12 +16,14 @@ export function getCollectedTypes() { } export async function register(options: IOptions = {}) { - const typewizConfigPath = path.resolve(options && options.typewizConfig ? options.typewizConfig : 'typewiz.json'); - const configurationParser = new typewiz.ConfigurationParser(); + const typewizConfigPath = + options && options.typewizConfig + ? path.resolve(options.typewizConfig) + : configurationParser.findConfigFile(process.cwd()); await configurationParser.parse(typewizConfigPath); - await nodeRegister.register(); + await nodeRegister.register({ typewizConfig: typewizConfigPath }); tsnode.register(options.tsNode); if (options.applyOnExit !== false) { process.on('exit', () => { diff --git a/packages/typewiz-node/src/node-register.ts b/packages/typewiz-node/src/node-register.ts index 468eb54..23551b5 100644 --- a/packages/typewiz-node/src/node-register.ts +++ b/packages/typewiz-node/src/node-register.ts @@ -1,15 +1,14 @@ -import * as path from 'path'; import { ConfigurationParser, instrument } from 'typewiz-core'; import { $_$twiz } from 'typewiz-core/dist/type-collector-snippet'; type ICompileFunction = (source: string, filename: string) => void; export interface IRegisterOptions { - typewizConfig?: string; + typewizConfig: string; extensions?: string[]; } -export async function register(options?: IRegisterOptions) { +export async function register(options: IRegisterOptions) { options = Object.assign( { extensions: ['.ts', '.tsx'], @@ -18,10 +17,8 @@ export async function register(options?: IRegisterOptions) { options, ); - const typewizConfigPath = path.resolve(options.typewizConfig!); - const configurationParser = new ConfigurationParser(); - await configurationParser.parse(typewizConfigPath); + await configurationParser.parse(options.typewizConfig); (global as any).$_$twiz = $_$twiz; diff --git a/packages/typewiz-webpack/src/loader.ts b/packages/typewiz-webpack/src/loader.ts index 6e6e767..9d53603 100644 --- a/packages/typewiz-webpack/src/loader.ts +++ b/packages/typewiz-webpack/src/loader.ts @@ -9,14 +9,15 @@ let configurationParser: ConfigurationParser; export async function typewizLoader(this: loader.LoaderContext, source: string | undefined) { const callback = this.async(); - const typewizConfigPath = path.resolve(getOptions(this).typewizConfig || 'typewiz.json'); - this.addDependency(typewizConfigPath); - if (promise) { await promise; } if (!configurationParser) { configurationParser = new ConfigurationParser(); + const typewizConfigPath = + getOptions(this) && getOptions(this).typewizConfig + ? path.resolve(getOptions(this).typewizConfig) + : configurationParser.findConfigFile(process.cwd()); promise = configurationParser.parse(typewizConfigPath); await promise; } From 1f10f8cdb21b46af72ea66e82f295949f7bd2eaa Mon Sep 17 00:00:00 2001 From: Tom Z?hner Date: Sun, 8 Apr 2018 15:11:02 +0200 Subject: [PATCH 14/17] Resolve compiler options relative to typewiz.json --- .../src/configuration-parser.spec.ts | 33 ++++++++++--------- .../typewiz-core/src/configuration-parser.ts | 11 ++++++- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/packages/typewiz-core/src/configuration-parser.spec.ts b/packages/typewiz-core/src/configuration-parser.spec.ts index db89496..a8b4f1e 100644 --- a/packages/typewiz-core/src/configuration-parser.spec.ts +++ b/packages/typewiz-core/src/configuration-parser.spec.ts @@ -42,8 +42,8 @@ describe('ConfigurationParser', () => { typewizConfig = ` { "commond": { - "rootDir": "...", - "tsConfig": "..." + "rootDir": ".", + "tsConfig": "./tsconfig.json" } } `; @@ -66,8 +66,8 @@ describe('ConfigurationParser', () => { typewizConfig = ` { "common": { - "rootDir": "...", - "tsConfig": "..." + "rootDir": ".", + "tsConfig": "./tsconfig.json" }, "instrument": { "instrumentCallExpressions": true, @@ -103,8 +103,8 @@ describe('ConfigurationParser', () => { typewizConfig = ` { "common": { - "rootDir": "...", - "tsConfig": "..." + "rootDir": ".", + "tsConfig": "./tsconfig.json" } } `; @@ -112,7 +112,10 @@ describe('ConfigurationParser', () => { await configParser.parse('test/typewiz.json'); return configParser.getCompilerOptions(); })(), - ).resolves.toEqual({ rootDir: '...', tsConfig: '...' }); + ).resolves.toEqual({ + rootDir: path.resolve('test', '.'), + tsConfig: path.resolve('test', './tsconfig.json'), + }); }); it('should return IInstrumentOptions if no configuration is specified', async () => { @@ -135,8 +138,8 @@ describe('ConfigurationParser', () => { typewizConfig = ` { "common": { - "rootDir": "...", - "tsConfig": "..." + "rootDir": ".", + "tsConfig": "./tsconfig.json" }, "instrument": { "instrumentCallExpressions": true, @@ -152,9 +155,9 @@ describe('ConfigurationParser', () => { ).resolves.toEqual({ instrumentCallExpressions: true, instrumentImplicitThis: true, - rootDir: '...', + rootDir: path.resolve('test', '.'), skipTwizDeclarations: true, - tsConfig: '...', + tsConfig: path.resolve('test', './tsconfig.json'), }); }); @@ -178,8 +181,8 @@ describe('ConfigurationParser', () => { typewizConfig = ` { "common": { - "rootDir": "...", - "tsConfig": "..." + "rootDir": ".", + "tsConfig": "./tsconfig.json" }, "applyTypes": { "prefix": "TypeWiz |" @@ -192,8 +195,8 @@ describe('ConfigurationParser', () => { })(), ).resolves.toEqual({ prefix: 'TypeWiz |', - rootDir: '...', - tsConfig: '...', + rootDir: path.resolve('test', '.'), + tsConfig: path.resolve('test', './tsconfig.json'), }); }); }); diff --git a/packages/typewiz-core/src/configuration-parser.ts b/packages/typewiz-core/src/configuration-parser.ts index c547bf1..54efa68 100644 --- a/packages/typewiz-core/src/configuration-parser.ts +++ b/packages/typewiz-core/src/configuration-parser.ts @@ -10,6 +10,7 @@ const readFileAsync = util.promisify(fs.readFile); export class ConfigurationParser { private typewizConfig: any; + private configurationPath: string | undefined; public findConfigFile(cwd: string): string { return ts.findConfigFile(cwd, ts.sys.fileExists, 'typewiz.json'); @@ -23,6 +24,7 @@ export class ConfigurationParser { let typewizConfigString; try { typewizConfigString = await readFileAsync(path.resolve(configurationPath), { encoding: 'utf8' }); + this.configurationPath = path.resolve(configurationPath); } catch (error) { typewizConfigString = '{}'; } @@ -43,7 +45,14 @@ export class ConfigurationParser { } public getCompilerOptions(): ICompilerOptions { - return { ...this.typewizConfig.common }; + const compilerOptions: ICompilerOptions = { ...this.typewizConfig.common }; + if (compilerOptions.rootDir && this.configurationPath) { + compilerOptions.rootDir = path.resolve(path.dirname(this.configurationPath), compilerOptions.rootDir); + } + if (compilerOptions.tsConfig && this.configurationPath) { + compilerOptions.tsConfig = path.resolve(path.dirname(this.configurationPath), compilerOptions.tsConfig); + } + return compilerOptions; } public getInstrumentOptions(): IInstrumentOptions { From be0a02c8629f32f59232b6b38343ae0e26b6ec6a Mon Sep 17 00:00:00 2001 From: zoehneto Date: Mon, 9 Apr 2018 08:42:11 +0200 Subject: [PATCH 15/17] Load typewiz.json schema with require --- packages/typewiz-core/src/configuration-parser.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/typewiz-core/src/configuration-parser.ts b/packages/typewiz-core/src/configuration-parser.ts index 54efa68..a9a984b 100644 --- a/packages/typewiz-core/src/configuration-parser.ts +++ b/packages/typewiz-core/src/configuration-parser.ts @@ -7,6 +7,7 @@ import { IApplyTypesOptions } from './apply-types'; import { ICompilerOptions } from './compiler-helper'; import { IInstrumentOptions } from './instrument'; const readFileAsync = util.promisify(fs.readFile); +const typewizConfigSchema = require('./typewiz.json'); // tslint:disable-line:no-var-requires export class ConfigurationParser { private typewizConfig: any; @@ -17,10 +18,6 @@ export class ConfigurationParser { } public async parse(configurationPath: string): Promise { - const typewizConfigSchema = JSON.parse( - await readFileAsync(path.join(__dirname, 'typewiz.json'), { encoding: 'utf8' }), - ); - let typewizConfigString; try { typewizConfigString = await readFileAsync(path.resolve(configurationPath), { encoding: 'utf8' }); From 507f1ac1f49fe47a9ad6bef541b78e6ed3ec086c Mon Sep 17 00:00:00 2001 From: zoehneto Date: Mon, 9 Apr 2018 09:45:48 +0200 Subject: [PATCH 16/17] Add parseSync method to configuration parser and use it in typewiz-node --- .../src/configuration-parser.spec.ts | 40 ++++++++++++++++++- .../typewiz-core/src/configuration-parser.ts | 34 +++++++++++----- packages/typewiz-node/src/index.ts | 6 +-- packages/typewiz-node/src/node-register.ts | 4 +- packages/typewiz-node/src/typewiz-node.ts | 8 ++-- 5 files changed, 71 insertions(+), 21 deletions(-) diff --git a/packages/typewiz-core/src/configuration-parser.spec.ts b/packages/typewiz-core/src/configuration-parser.spec.ts index a8b4f1e..f4650a1 100644 --- a/packages/typewiz-core/src/configuration-parser.spec.ts +++ b/packages/typewiz-core/src/configuration-parser.spec.ts @@ -15,17 +15,32 @@ const mockFs = { } }, ), + readFileSync: jest.fn((filePath: string, options: any) => { + if ( + filePath === path.resolve('packages', 'typewiz-core', 'src', 'typewiz.json') || + filePath === path.resolve('not-found-file.json') + ) { + return fs.readFileSync(filePath, options); + } else { + return typewizConfig; + } + }), }; jest.doMock('fs', () => mockFs); import { ConfigurationParser } from './configuration-parser'; describe('ConfigurationParser', () => { - it('should handle a non-existing typewiz.json file by using an empty default config', async () => { + it('should handle a non-existing typewiz.json file by using an empty default config - async', async () => { const configParser = new ConfigurationParser(); await configParser.parse('not-found-file.json'); }); + it('should handle a non-existing typewiz.json file by using an empty default config - sync', () => { + const configParser = new ConfigurationParser(); + configParser.parseSync('not-found-file.json'); + }); + it('should throw an error if given bad typewiz.json file', async () => { await expect( (() => { @@ -97,7 +112,7 @@ describe('ConfigurationParser', () => { ).resolves.toEqual({}); }); - it('should return an ICompilerConfig if a configuration is specified', async () => { + it('should return an ICompilerConfig if a configuration is specified - async', async () => { await expect( (async () => { typewizConfig = ` @@ -118,6 +133,27 @@ describe('ConfigurationParser', () => { }); }); + it('should return an ICompilerConfig if a configuration is specified - sync', () => { + expect( + (() => { + typewizConfig = ` + { + "common": { + "rootDir": ".", + "tsConfig": "./tsconfig.json" + } + } + `; + const configParser = new ConfigurationParser(); + configParser.parseSync('test/typewiz.json'); + return configParser.getCompilerOptions(); + })(), + ).toEqual({ + rootDir: path.resolve('test', '.'), + tsConfig: path.resolve('test', './tsconfig.json'), + }); + }); + it('should return IInstrumentOptions if no configuration is specified', async () => { await expect( (async () => { diff --git a/packages/typewiz-core/src/configuration-parser.ts b/packages/typewiz-core/src/configuration-parser.ts index a9a984b..9392e4d 100644 --- a/packages/typewiz-core/src/configuration-parser.ts +++ b/packages/typewiz-core/src/configuration-parser.ts @@ -26,19 +26,19 @@ export class ConfigurationParser { typewizConfigString = '{}'; } - let typewizConfig; + this.parseConfig(typewizConfigString); + } + + public parseSync(configurationPath: string): void { + let typewizConfigString; try { - typewizConfig = JSON.parse(typewizConfigString); + typewizConfigString = fs.readFileSync(path.resolve(configurationPath), { encoding: 'utf8' }); + this.configurationPath = path.resolve(configurationPath); } catch (error) { - throw new Error('Could not parser configuration file: ' + error.message); + typewizConfigString = '{}'; } - const ajv = new Ajv(); - const valid = ajv.validate(typewizConfigSchema, typewizConfig); - if (!valid) { - throw new Error(ajv.errorsText(ajv.errors, { dataVar: 'typewiz.json' })); - } - this.typewizConfig = typewizConfig; + this.parseConfig(typewizConfigString); } public getCompilerOptions(): ICompilerOptions { @@ -59,4 +59,20 @@ export class ConfigurationParser { public getApplyTypesOptions(): IApplyTypesOptions { return { ...this.getCompilerOptions(), ...this.typewizConfig.applyTypes }; } + + private parseConfig(typewizConfigString: string): void { + let typewizConfig; + try { + typewizConfig = JSON.parse(typewizConfigString); + } catch (error) { + throw new Error('Could not parser configuration file: ' + error.message); + } + + const ajv = new Ajv(); + const valid = ajv.validate(typewizConfigSchema, typewizConfig); + if (!valid) { + throw new Error(ajv.errorsText(ajv.errors, { dataVar: 'typewiz.json' })); + } + this.typewizConfig = typewizConfig; + } } diff --git a/packages/typewiz-node/src/index.ts b/packages/typewiz-node/src/index.ts index 8164a81..6167339 100644 --- a/packages/typewiz-node/src/index.ts +++ b/packages/typewiz-node/src/index.ts @@ -15,15 +15,15 @@ export function getCollectedTypes() { return $_$twiz.get(); } -export async function register(options: IOptions = {}) { +export function register(options: IOptions = {}) { const configurationParser = new typewiz.ConfigurationParser(); const typewizConfigPath = options && options.typewizConfig ? path.resolve(options.typewizConfig) : configurationParser.findConfigFile(process.cwd()); - await configurationParser.parse(typewizConfigPath); + configurationParser.parseSync(typewizConfigPath); - await nodeRegister.register({ typewizConfig: typewizConfigPath }); + nodeRegister.register({ typewizConfig: typewizConfigPath }); tsnode.register(options.tsNode); if (options.applyOnExit !== false) { process.on('exit', () => { diff --git a/packages/typewiz-node/src/node-register.ts b/packages/typewiz-node/src/node-register.ts index 23551b5..6350c6a 100644 --- a/packages/typewiz-node/src/node-register.ts +++ b/packages/typewiz-node/src/node-register.ts @@ -8,7 +8,7 @@ export interface IRegisterOptions { extensions?: string[]; } -export async function register(options: IRegisterOptions) { +export function register(options: IRegisterOptions) { options = Object.assign( { extensions: ['.ts', '.tsx'], @@ -18,7 +18,7 @@ export async function register(options: IRegisterOptions) { ); const configurationParser = new ConfigurationParser(); - await configurationParser.parse(options.typewizConfig); + configurationParser.parseSync(options.typewizConfig); (global as any).$_$twiz = $_$twiz; diff --git a/packages/typewiz-node/src/typewiz-node.ts b/packages/typewiz-node/src/typewiz-node.ts index 1287c5b..77bffd6 100644 --- a/packages/typewiz-node/src/typewiz-node.ts +++ b/packages/typewiz-node/src/typewiz-node.ts @@ -2,9 +2,7 @@ import { register } from './index'; -(async () => { - await register(); +register(); - // Delegate the rest of the fun to ts-node: - require('ts-node/dist/_bin'); // tslint:disable-line:no-var-requires -})(); +// Delegate the rest of the fun to ts-node: +require('ts-node/dist/_bin'); // tslint:disable-line:no-var-requires From 9bb3152538cc9be5d09cf5ea0436b66277ddf526 Mon Sep 17 00:00:00 2001 From: zoehneto Date: Mon, 9 Apr 2018 10:09:31 +0200 Subject: [PATCH 17/17] Update docs for typewiz-webpack --- packages/typewiz-webpack/README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/typewiz-webpack/README.md b/packages/typewiz-webpack/README.md index a9dc635..04547ac 100644 --- a/packages/typewiz-webpack/README.md +++ b/packages/typewiz-webpack/README.md @@ -32,6 +32,27 @@ Then, add `typewiz-webpack` to your list of loaders, just after your usual TypeS }, ``` +Optionally you can also specify the path to your typewiz.json file: + +```javascript + module: { + rules: [ + { + test: /\.tsx?$/, + loaders:[ + 'awesome-typescript-loader', + { + loader:'typewiz-webpack', + options:{ + typewizConfig: path.resolve(__dirname, "../typewiz.json") + } + } + ] + } + ] + }, +``` + Next, add `TypewizPlugin` to your list of plugins, and configure your webpack-dev-server to save the collected type info to disk using the provided `typewizCollectorMiddleware` middleware. Here is an example of what your final config file may look like: