This repository has been archived by the owner on Oct 5, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #55 from zoehneto/config-file
feat: config file [WIP]
- Loading branch information
Showing
17 changed files
with
494 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,6 @@ | ||
language: node_js | ||
sudo: false | ||
node_js: | ||
- 6.4.0 | ||
- 8 | ||
- stable | ||
script: | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
import * as fs from 'fs'; | ||
import * as path from 'path'; | ||
|
||
let typewizConfig = ''; | ||
const mockFs = { | ||
readFile: jest.fn( | ||
(filePath: string, options: any, callback: (err: NodeJS.ErrnoException, data: string) => void) => { | ||
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); | ||
} | ||
}, | ||
), | ||
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', 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( | ||
(() => { | ||
typewizConfig = '<invalid json>'; | ||
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', async () => { | ||
await expect( | ||
(() => { | ||
typewizConfig = ` | ||
{ | ||
"commond": { | ||
"rootDir": ".", | ||
"tsConfig": "./tsconfig.json" | ||
} | ||
} | ||
`; | ||
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', async () => { | ||
typewizConfig = ` | ||
{ | ||
} | ||
`; | ||
const configParser = new ConfigurationParser(); | ||
await configParser.parse('test/typewiz.json'); | ||
}); | ||
|
||
it('should parse a valid typewiz.json file with all options set', async () => { | ||
typewizConfig = ` | ||
{ | ||
"common": { | ||
"rootDir": ".", | ||
"tsConfig": "./tsconfig.json" | ||
}, | ||
"instrument": { | ||
"instrumentCallExpressions": true, | ||
"instrumentImplicitThis": true, | ||
"skipTwizDeclarations": true | ||
}, | ||
"applyTypes": { | ||
"prefix": "TypeWiz |" | ||
} | ||
} | ||
`; | ||
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', async () => { | ||
await expect( | ||
(async () => { | ||
typewizConfig = ` | ||
{ | ||
"common": { | ||
"rootDir": ".", | ||
"tsConfig": "./tsconfig.json" | ||
} | ||
} | ||
`; | ||
const configParser = new ConfigurationParser(); | ||
await configParser.parse('test/typewiz.json'); | ||
return configParser.getCompilerOptions(); | ||
})(), | ||
).resolves.toEqual({ | ||
rootDir: path.resolve('test', '.'), | ||
tsConfig: path.resolve('test', './tsconfig.json'), | ||
}); | ||
}); | ||
|
||
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 () => { | ||
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": "./tsconfig.json" | ||
}, | ||
"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: path.resolve('test', '.'), | ||
skipTwizDeclarations: true, | ||
tsConfig: path.resolve('test', './tsconfig.json'), | ||
}); | ||
}); | ||
|
||
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": "./tsconfig.json" | ||
}, | ||
"applyTypes": { | ||
"prefix": "TypeWiz |" | ||
} | ||
} | ||
`; | ||
const configParser = new ConfigurationParser(); | ||
await configParser.parse('test/typewiz.json'); | ||
return configParser.getApplyTypesOptions(); | ||
})(), | ||
).resolves.toEqual({ | ||
prefix: 'TypeWiz |', | ||
rootDir: path.resolve('test', '.'), | ||
tsConfig: path.resolve('test', './tsconfig.json'), | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
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'; | ||
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; | ||
private configurationPath: string | undefined; | ||
|
||
public findConfigFile(cwd: string): string { | ||
return ts.findConfigFile(cwd, ts.sys.fileExists, 'typewiz.json'); | ||
} | ||
|
||
public async parse(configurationPath: string): Promise<void> { | ||
let typewizConfigString; | ||
try { | ||
typewizConfigString = await readFileAsync(path.resolve(configurationPath), { encoding: 'utf8' }); | ||
this.configurationPath = path.resolve(configurationPath); | ||
} catch (error) { | ||
typewizConfigString = '{}'; | ||
} | ||
|
||
this.parseConfig(typewizConfigString); | ||
} | ||
|
||
public parseSync(configurationPath: string): void { | ||
let typewizConfigString; | ||
try { | ||
typewizConfigString = fs.readFileSync(path.resolve(configurationPath), { encoding: 'utf8' }); | ||
this.configurationPath = path.resolve(configurationPath); | ||
} catch (error) { | ||
typewizConfigString = '{}'; | ||
} | ||
|
||
this.parseConfig(typewizConfigString); | ||
} | ||
|
||
public getCompilerOptions(): ICompilerOptions { | ||
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 { | ||
return { ...this.getCompilerOptions(), ...this.typewizConfig.instrument }; | ||
} | ||
|
||
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
export * from './apply-types'; | ||
export * from './configuration-parser'; | ||
export * from './instrument'; | ||
export * from './node-register'; | ||
export * from './type-collector'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.