Skip to content

Commit

Permalink
Implement cascade configuration (#2320)
Browse files Browse the repository at this point in the history
* Implement cascade configuration

* Update documentation
  • Loading branch information
AlbertoBrusa authored Mar 22, 2023
1 parent 8365b77 commit 561d780
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 11 deletions.
6 changes: 6 additions & 0 deletions .changeset/dry-mirrors-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'modular-scripts': minor
---

Allow root level configuration that's overrideable by package-specific
configuration
File renamed without changes.
3 changes: 3 additions & 0 deletions __fixtures__/test-config/webpack-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
useModularEsbuild: false,
};
4 changes: 3 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ behaviours. Additionally it requires some minimal configuration within the

We allow a number of Modular behaviours to be configured via a dedicated Modular
config file, `.modular.js`, that can be placed at the root of a
workspace/package you wish to configure.
workspace/package you wish to configure, or at the root of your project for a
'global' configuration file that affects all packages. Root configurations will
be overridden by local package configurations if both are provided.

We support the following file names/formats:

Expand Down
33 changes: 25 additions & 8 deletions packages/modular-scripts/src/__tests__/utils/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,45 @@ const configFixtures = path.join(modularRoot, '__fixtures__', 'test-config');
// Temporary test context paths set by createTempModularRepoWithTemplate()
let tempModularRepo: string;

describe('A simple modular repo with a .modular.js config file', () => {
const app = 'test-app';
const esbuildConfigFile = 'esbuild-config.js';
const webpackConfigFile = 'webpack-config.js';

describe('A modular repo with a root .modular.js config file', () => {
beforeEach(() => {
tempModularRepo = createModularTestContext();
runModularForTests(tempModularRepo, 'add test-app --unstable-type app');
runModularForTests(tempModularRepo, `add ${app} --unstable-type app`);
copyFileSync(
path.join(configFixtures, '.modular.js'),
path.join(tempModularRepo, 'packages', 'test-app', '.modular.js'),
path.join(configFixtures, esbuildConfigFile),
path.join(tempModularRepo, '.modular.js'),
);
});
it('builds using esbuild as specified in config file', () => {
it('builds using esbuild as specified in the root config file', () => {
const result = runModularPipeLogs(
tempModularRepo,
`build test-app --verbose`,
`build ${app} --verbose`,
'true',
);
expect(result.stdout).toContain('Building with esbuild');
expect(result.exitCode).toBe(0);
});
it('builds using webpack if the environment variable is provided as it overrides the config', () => {
it('builds using webpack if a package specific config file overrides the root config', () => {
copyFileSync(
path.join(configFixtures, webpackConfigFile),
path.join(tempModularRepo, 'packages', app, '.modular.js'),
);
const result = runModularPipeLogs(
tempModularRepo,
`build ${app} --verbose`,
'true',
);
expect(result.stdout).toContain('Building with Webpack');
expect(result.exitCode).toBe(0);
});
it('builds using webpack if an environment variable is provided as it overrides the config', () => {
const result = runModularPipeLogs(
tempModularRepo,
`build test-app --verbose`,
`build ${app} --verbose`,
'true',
{
env: {
Expand Down
42 changes: 40 additions & 2 deletions packages/modular-scripts/src/utils/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { cosmiconfigSync } from 'cosmiconfig';
import path from 'path';
import getModularRoot from './getModularRoot';
import * as logger from './logger';
const modularRoot = getModularRoot();

// Where cosmiconfig can look for the configuration
const searchPlaces = [
Expand Down Expand Up @@ -106,7 +109,8 @@ function searchConfig(
* @param workspacePath Path of workspace to which configuration applies
* @returns configured value:
* - the override environment variable if configured
* - the value stated in the config file if provided
* - the value stated in the package's config file if provided
* - the value stated in the root config file if provided
* - the default value if neither environment variable nor the config file are provided
*/
export function getConfig<T extends keyof Config>(
Expand All @@ -115,5 +119,39 @@ export function getConfig<T extends keyof Config>(
): Config[T] {
const configResult = searchConfig(workspacePath);
const configValue = configResult ? configResult.config[field] : undefined;
return defs[field].override ?? configValue ?? defs[field].default;
const rootConfigResult = searchConfig(modularRoot);
const rootConfigValue = rootConfigResult
? rootConfigResult.config[field]
: undefined;
const returnConfigValue =
defs[field].override ??
configValue ??
rootConfigValue ??
defs[field].default;
if (defs[field].override !== undefined) {
logger.debug(
`${field} configured to ${JSON.stringify(
returnConfigValue,
)} by the environment variable provided`,
);
} else if (configValue !== undefined) {
logger.debug(
`${field} configured to ${JSON.stringify(
returnConfigValue,
)} through package's modular configuration at ${workspacePath}`,
);
} else if (rootConfigValue !== undefined) {
logger.debug(
`${field} configured to ${JSON.stringify(
returnConfigValue,
)} through the project's root modular configuration`,
);
} else {
logger.debug(
`Using default configuration for ${field}: ${JSON.stringify(
returnConfigValue,
)}`,
);
}
return returnConfigValue;
}

0 comments on commit 561d780

Please sign in to comment.