Skip to content

Commit

Permalink
feat(utils): normalizeCompilerOptions
Browse files Browse the repository at this point in the history
Signed-off-by: Lexus Drumgold <unicornware@flexdevelopment.llc>
  • Loading branch information
unicornware committed Feb 7, 2023
1 parent fd394d8 commit 1714d5b
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 0 deletions.
101 changes: 101 additions & 0 deletions src/utils/__tests__/normalize-compiler-options.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* @file Unit Tests - normalizeCompilerOptions
* @module tsconfig-utils/utils/tests/unit/normalizeCompilerOptions
*/

import {
ImportsNotUsedKind,
JsxEmit,
ModuleDetectionKind,
ModuleKind,
ModuleResolutionKind,
NewLineKind,
ScriptTarget,
type CompilerOptions
} from '@flex-development/tsconfig-types'
import ts from 'typescript'
import LIB from '../lib'
import testSubject from '../normalize-compiler-options'

describe('unit:utils/normalizeCompilerOptions', () => {
it('should return empty object if compilerOptions schema is invalid', () => {
// Arrange
const cases: Parameters<typeof testSubject>[] = [
[faker.datatype.array()],
[faker.string.sample()]
]

// Act + Expect
cases.forEach(([compilerOptions]) => {
expect(testSubject(compilerOptions)).to.deep.equal({})
})
})

it('should return normalized compiler options', () => {
// Arrange
const compilerOptions: CompilerOptions = {
allowJs: true,
allowUnreachableCode: false,
alwaysStrict: false,
baseUrl: '.',
checkJs: false,
declaration: true,
declarationMap: true,
emitDecoratorMetadata: true,
esModuleInterop: true,
exactOptionalPropertyTypes: true,
experimentalDecorators: true,
forceConsistentCasingInFileNames: true,
importsNotUsedAsValues: ImportsNotUsedKind.Error,
isolatedModules: true,
jsx: JsxEmit.ReactJSX,
lib: ['dom', 'dom.iterable', 'es2020'],
module: ModuleKind.ESNext,
moduleDetection: ModuleDetectionKind.Force,
moduleResolution: ModuleResolutionKind.NodeNext,
newLine: NewLineKind.LineFeed,
noEmit: true,
noErrorTruncation: true,
noFallthroughCasesInSwitch: true,
noImplicitAny: true,
noImplicitOverride: true,
noImplicitReturns: true,
noUncheckedIndexedAccess: true,
noUnusedLocals: false,
noUnusedParameters: false,
outDir: 'dist',
paths: {
'#fixtures/*': ['__fixtures__/*'],
'#src': ['src/index'],
'#src/*': ['src/*'],
'#tests/*': ['__tests__/*']
},
preserveConstEnums: true,
preserveSymlinks: true,
pretty: true,
resolveJsonModule: true,
rootDir: '.',
skipLibCheck: true,
sourceMap: true,
strict: true,
strictNullChecks: true,
strictPropertyInitialization: true,
target: ScriptTarget.ESNext,
useDefineForClassFields: true,
useUnknownInCatchVariables: true
}

// Act + Expect
expect(testSubject({ ...compilerOptions, faker })).to.deep.equal({
...compilerOptions,
importsNotUsedAsValues: ts.ImportsNotUsedAsValues.Error,
jsx: ts.JsxEmit.ReactJSX,
lib: [LIB.get('dom'), LIB.get('dom.iterable'), LIB.get('es2020')],
module: ts.ModuleKind.ESNext,
moduleDetection: ts.ModuleDetectionKind.Force,
moduleResolution: ts.ModuleResolutionKind.NodeNext,
newLine: ts.NewLineKind.LineFeed,
target: ts.ScriptTarget.ESNext
})
})
})
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export { default as loadLibConfig } from './load-lib-config'
export { default as loadPathAliases } from './load-path-aliases'
export { default as loadPluginConfigs } from './load-plugin-configs'
export { default as loadTsconfig } from './load-tsconfig'
export { default as normalizeCompilerOptions } from './normalize-compiler-options'
export { default as normalizeImportsNotUsed } from './normalize-imports-not-used'
export { default as normalizeJsx } from './normalize-jsx'
export { default as normalizeLib } from './normalize-lib'
Expand Down
98 changes: 98 additions & 0 deletions src/utils/normalize-compiler-options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/**
* @file Utilities - normalizeCompilerOptions
* @module tsconfig-utils/utils/normalizeCompilerOptions
*/

import type {
CompilerOptions,
CompilerOptionsValue
} from '@flex-development/tsconfig-types'
import { isNIL, isPrimitive, type Nilable } from '@flex-development/tutils'
import sortKeys from 'sort-keys'
import type ts from 'typescript'
import COMPILER_OPTIONS from './compiler-options'
import normalizeImportsNotUsed from './normalize-imports-not-used'
import normalizeJsx from './normalize-jsx'
import normalizeLib from './normalize-lib'
import normalizeModule from './normalize-module'
import normalizeModuleDetection from './normalize-module-detection'
import normalizeModuleResolution from './normalize-module-resolution'
import normalizeNewLine from './normalize-new-line'
import normalizeTarget from './normalize-target'

/**
* Converts the given user [`compilerOptions`][1] into **programmatic** compiler
* options.
*
* The TypeScript program expects `compilerOptions` to use enum values where
* appropriate.
*
* [1]: https://www.typescriptlang.org/tsconfig#compilerOptions
*
* @param {unknown} compilerOptions - User compiler options
* @return {ts.CompilerOptions} TypeScript program compiler options
*/
const normalizeCompilerOptions = (
compilerOptions: unknown
): ts.CompilerOptions => {
// exit early if compilerOptions schema is invalid
if (Array.isArray(compilerOptions) || isPrimitive(compilerOptions)) return {}

/**
* TypeScript program compiler options.
*
* @const {ts.CompilerOptions} ret
*/
const ret: ts.CompilerOptions = {}

/**
* Sets a compiler option on {@linkcode ret}.
*
* Does nothing if the given `value` is `null` or `undefined`.
*
* @param {keyof ts.CompilerOptions} option - Compiler option name
* @param {Nilable<CompilerOptionsValue>} value - Compiler option value
* @return {void} Nothing when complete
*/
const set = (
option: keyof ts.CompilerOptions,
value: Nilable<CompilerOptionsValue>
): void => void (!isNIL(value) && (ret[option] = value))

// get programmatic options
for (const [key, val] of Object.entries(compilerOptions as CompilerOptions)) {
switch (key) {
case 'importsNotUsedAsValues':
set(key, normalizeImportsNotUsed(val))
break
case 'jsx':
set(key, normalizeJsx(val))
break
case 'lib':
set(key, normalizeLib(val))
break
case 'module':
set(key, normalizeModule(val))
break
case 'moduleDetection':
set(key, normalizeModuleDetection(val))
break
case 'moduleResolution':
set(key, normalizeModuleResolution(val))
break
case 'newLine':
set(key, normalizeNewLine(val))
break
case 'target':
set(key, normalizeTarget(val))
break
default:
COMPILER_OPTIONS.has(key) && set(key, val)
break
}
}

return sortKeys(ret, { deep: true })
}

export default normalizeCompilerOptions

0 comments on commit 1714d5b

Please sign in to comment.