@lipemat/js-boilerplate
Version:
Dependencies and scripts for a no config JavaScript app
118 lines (105 loc) • 3.57 kB
text/typescript
import {existsSync} from 'fs';
import {resolve} from 'path';
import {type Configuration as WebpackConfig} from 'webpack';
import type {Configuration as DevServerConfig} from 'webpack-dev-server';
import type {BabelConfig} from '../config/babel.config';
import type {JestConfig} from '../config/jest.config.js';
import {getPackageConfig} from '@lipemat/js-boilerplate-shared/helpers/package-config.js';
import type {EntriesConfig} from '../config/entries.config.js';
import type {CssLoaderConfig} from '../config/css-loader.config.js';
import {ensureJSExtension, getExtensionsConfig, mergeWithLocalConfig} from '@lipemat/js-boilerplate-shared/helpers/config.js';
type Configs = {
'babel.config.js': BabelConfig;
'css-loader.config.js': CssLoaderConfig;
'dev-server.config.js': DevServerConfig;
'entries.config.js': EntriesConfig;
'jest.config.js': JestConfig;
'webpack.dev.js': WebpackConfig;
'webpack.dist.js': WebpackConfig;
};
const {workingDirectory, packageDirectory} = getPackageConfig();
/**
* Check to see if a local config file exists.
*
* @param {string} fileName
* @param {boolean} inWorkingDirectory - Look in working directory instead of their /config directory
*
* @return {boolean}
*/
export async function hasLocalOverride( fileName: string, inWorkingDirectory: boolean = false ): Promise<boolean> {
let hasLocal = false;
try {
if ( inWorkingDirectory ) {
await import( resolve( workingDirectory, fileName ) );
hasLocal = true;
} else {
await import( resolve( packageDirectory + '/config', fileName ) );
hasLocal = true;
}
} catch ( e ) {
if ( e instanceof Error ) {
if ( ! ( 'code' in e ) || 'MODULE_NOT_FOUND' !== e.code ) {
console.error( e );
}
}
}
return hasLocal;
}
/**
* Get a config from our /config directory merged with any
* matching configuration from the project directory.
*
* For instance, if we have a file named config/babel.config.js in our project,
* we will merge the contents with our config/babel.config.js in favor of whatever
* is specified with the project's file.
*
* If the `module.exports` is a function, the existing configuration will be passed
* as the only argument. Otherwise, standard `module.exports` are also supported.
*
* @example ```ts
* // standard
* module.export = {
* externals: {extra: 'Extra'}
* }
* // function
* module.exports = function(config) {
* return {
* externals: {...config.externals, extra: 'Extra'}
* }
* }
* ```
*
* @param {string} fileName
*
* @return {Object}
*/
export async function getConfig<T extends keyof Configs>( fileName: T ): Promise<Configs[T]> {
const configModule = await import( ensureJSExtension( `../config/${fileName}` ) );
const config = configModule.default;
const extensionsConfig = getExtensionsConfig<Configs[T]>( fileName, config );
const mergedConfig: Configs[T] = {...config, ...extensionsConfig};
return mergeWithLocalConfig<Configs[T]>( fileName, mergedConfig );
}
/**
* Get the path to the "tsconfig.json" file if it exists.
*
* 1. The working directory.
* 2. The package directory.
*
* The package directory takes priority over the working directory.
*
*
* @return {string}
*/
export function getTsConfigFile(): string {
const possibles = [
// Backward compatible for before @lipemat/eslint-config version 3.
resolve( workingDirectory + '/tsconfig.json' ),
resolve( packageDirectory + '/tsconfig.json' ),
].filter( existsSync );
let tsConfig = '';
possibles.forEach( filePath => {
tsConfig = filePath;
} );
return tsConfig;
}