Skip to content

Commit

Permalink
Generate config automatically for create-react-app (#512)
Browse files Browse the repository at this point in the history
* Make getCosmosConfig a named export #452

* Add config generation for CRA #452

* Generate config when starting server #452
  • Loading branch information
ovidiuch committed Nov 13, 2017
1 parent 7f160c5 commit 8a5c65c
Show file tree
Hide file tree
Showing 38 changed files with 358 additions and 133 deletions.
5 changes: 5 additions & 0 deletions packages/react-cosmos-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,10 @@
"react-cosmos-shared": "^3.0.0-beta.13",
"yargs": "^9.0.1"
},
"devDependencies": {
"mkdirp": "^0.5.1",
"rimraf": "^2.6.2",
"touch": "^3.1.0"
},
"xo": false
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @flow

import path from 'path';
import getCosmosConfig from '../../';
import { getCosmosConfig } from '../../';

const configPath = require.resolve(
'./__fsmocks__/cosmos-custom-root.config.js'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @flow

import path from 'path';
import getCosmosConfig from '../../';
import { getCosmosConfig } from '../../';

const configPath = require.resolve('./__fsmocks__/cosmos.config.js');
const rootPath = path.dirname(configPath);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @flow

import path from 'path';
import getCosmosConfig from '../../../';
import { getCosmosConfig } from '../../../';

jest.mock('yargs', () => ({ argv: { config: 'nested/cosmos.config' } }));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @flow

import path from 'path';
import getCosmosConfig from '../../../';
import { getCosmosConfig } from '../../../';

jest.mock('yargs', () => ({ argv: {} }));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @flow

import path from 'path';
import getCosmosConfig from '../../../';
import { getCosmosConfig } from '../../../';

jest.mock('yargs', () => ({ argv: { config: 'cozmos.config' } }));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @flow

import path from 'path';
import getCosmosConfig from '../../../';
import { getCosmosConfig, hasUserCosmosConfig } from '../../../';

jest.mock('yargs', () => ({ argv: {} }));

Expand All @@ -26,4 +26,8 @@ describe('[CLI path] when no config exists', () => {
webpackConfigPath: path.join(mocksPath, 'webpack.config')
});
});

it('reports users has no config', () => {
expect(hasUserCosmosConfig()).toBe(false);
});
});
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// @flow

import path from 'path';
import mkdirp from 'mkdirp';
import touch from 'touch';
import rimraf from 'rimraf';
import { generateCosmosConfig } from '../../../';

jest.mock('yargs', () => ({ argv: {} }));

// Note: __fsoutput__ are in Jest's watchPathIgnorePatterns
const outputPath = path.join(__dirname, '__fsoutput__');
const craWebpackConfigPath = path.join(
outputPath,
'node_modules/react-scripts/config/webpack.config.dev'
);
const cosmosConfigPath = path.join(outputPath, 'cosmos.config.js');

beforeEach(() => {
global.process.cwd = () => outputPath;

mkdirp.sync(path.dirname(craWebpackConfigPath));
touch.sync(craWebpackConfigPath);
});

afterEach(() => {
rimraf.sync(path.join(outputPath, 'node_modules'));
rimraf.sync(cosmosConfigPath);
});

describe('Create React App config generation', () => {
it('returns correct name', () => {
const generatedConfigFor = generateCosmosConfig();
expect(generatedConfigFor).toBe('Create React App');
});

it('creates config file', () => {
generateCosmosConfig();
expect(require(cosmosConfigPath)).toEqual({
containerQuerySelector: '#root',
webpackConfigPath: 'react-scripts/config/webpack.config.dev',
publicPath: 'public',
proxiesPath: 'src/cosmos.proxies'
});
});
});
10 changes: 10 additions & 0 deletions packages/react-cosmos-config/src/config-templates.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// @flow

export const CRA_COSMOS_CONFIG = `module.exports = {
containerQuerySelector: '#root',
webpackConfigPath: 'react-scripts/config/webpack.config.dev',
publicPath: 'public',
// Optional: Create this file when you begin adding proxies
proxiesPath: 'src/cosmos.proxies'
};
`;
44 changes: 39 additions & 5 deletions packages/react-cosmos-config/src/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// @flow

import fs from 'fs';
import path from 'path';
import { argv } from 'yargs';
import { importModule } from 'react-cosmos-shared';
import { moduleExists, resolveUserPath } from 'react-cosmos-shared/lib/server';
import { log, warn } from './log';
import { CRA_COSMOS_CONFIG } from './config-templates';

import type { ExcludePatterns } from 'react-cosmos-shared/src/types';

Expand Down Expand Up @@ -47,13 +49,11 @@ const defaults = {
fixturePaths: []
};

export default function getCosmosConfig(cosmosConfigPath?: string): Config {
const configPath =
cosmosConfigPath ||
resolveUserPath(process.cwd(), argv.config || 'cosmos.config');
export function getCosmosConfig(cosmosConfigPath?: string): Config {
const configPath = getUserConfigPath(cosmosConfigPath);
const relPath = path.dirname(configPath);

if (!moduleExists(configPath)) {
if (!configExist(configPath)) {
if (argv.config) {
const relPath = path.relative(process.cwd(), configPath);
warn(`[Cosmos] No config file found at ${relPath}, using defaults`);
Expand All @@ -73,6 +73,40 @@ export default function getCosmosConfig(cosmosConfigPath?: string): Config {
return getNormalizedConfig(config, relPath);
}

export function hasUserCosmosConfig(): boolean {
return configExist(getUserConfigPath());
}

export function generateCosmosConfig(): ?string {
// Warning: This code assumes the user hasn't created cosmos.config by now
const configPath = getUserConfigPath();
const rootPath = path.dirname(configPath);
const craWebpackConfigPath = 'react-scripts/config/webpack.config.dev';

if (moduleExists(resolveUserPath(rootPath, craWebpackConfigPath))) {
fs.writeFileSync(configPath, CRA_COSMOS_CONFIG, 'utf8');

return 'Create React App';
}
}

function getUserConfigPath(customConfigPath?: string) {
const loosePath = path.resolve(
process.cwd(),
customConfigPath || argv.config || 'cosmos.config.js'
);

// Ensure path has file extension
return loosePath.match(/\.js$/) ? loosePath : `${loosePath}.js`;
}

function configExist(path: string): boolean {
// Only resolve config path once we know it exists, otherwise the path will
// be cached to a missing module for the rest of the process execution.
// This allows us to generate the config at run time and import it later.
return fs.existsSync(path);
}

function getNormalizedConfig(relativeConfig: Config, relPath: string): Config {
const {
globalImports,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import { readFileSync, writeFileSync } from 'fs';
import upgradeFixtures from '../upgrade-fixtures';
import { addComponentToFixture } from '../transforms/add-component-to-fixture';

jest.mock('react-cosmos-config', () =>
jest.fn(() => ({
jest.mock('react-cosmos-config', () => ({
hasUserCosmosConfig: () => true,
getCosmosConfig: jest.fn(() => ({
componentPaths: ['foo']
}))
);
}));

const mockComponents = {
'ill/shared/emoji-block': '/path/to/components/ill/shared/emoji-block.js',
Expand Down
2 changes: 1 addition & 1 deletion packages/react-cosmos-scripts/src/upgrade-fixtures.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import fs from 'fs';
import path from 'path';
import camelCase from 'lodash.camelcase';
import upperFirst from 'lodash.upperfirst';
import getCosmosConfig from 'react-cosmos-config';
import { getCosmosConfig } from 'react-cosmos-config';
import getFilePaths from 'react-cosmos-voyager';
import { addComponentToFixture } from './transforms/add-component-to-fixture';

Expand Down
2 changes: 1 addition & 1 deletion packages/react-cosmos-telescope/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import renderer from 'react-test-renderer';
import createStateProxy from 'react-cosmos-state-proxy';
import { importModule } from 'react-cosmos-shared';
import { moduleExists } from 'react-cosmos-shared/lib/server';
import getCosmosConfig from 'react-cosmos-config';
import { getCosmosConfig } from 'react-cosmos-config';
import { findFixtureFiles } from 'react-cosmos-voyager2/lib/server';
import { getComponents } from 'react-cosmos-voyager2/lib/client';
import { Loader } from 'react-cosmos-loader';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ const embedModules = require('../../embed-modules-webpack-loader');
const mockFileMatch = [];
const mockExclude = [];

jest.mock('react-cosmos-config', () => () => ({
rootPath: 'MOCK_ROOT_PATH',
fileMatch: mockFileMatch,
exclude: mockExclude,
componentPaths: [],
proxiesPath: require.resolve('../__fsmocks__/cosmos.proxies')
jest.mock('react-cosmos-config', () => ({
hasUserCosmosConfig: () => true,
getCosmosConfig: () => ({
rootPath: 'MOCK_ROOT_PATH',
fileMatch: mockFileMatch,
exclude: mockExclude,
componentPaths: [],
proxiesPath: require.resolve('../__fsmocks__/cosmos.proxies')
})
}));

const mockFixtureFiles = [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
// Requiring because embed-modules-webpack-loader is a CJS module
const embedModules = require('../../embed-modules-webpack-loader');

jest.mock('react-cosmos-config', () => () => ({
componentPaths: ['/path/to/components'],
proxiesPath: require.resolve('../__fsmocks__/cosmos.proxies')
jest.mock('react-cosmos-config', () => ({
hasUserCosmosConfig: () => true,
getCosmosConfig: () => ({
componentPaths: ['/path/to/components'],
proxiesPath: require.resolve('../__fsmocks__/cosmos.proxies')
})
}));

jest.mock('react-cosmos-voyager', () => () => ({
Expand Down
13 changes: 8 additions & 5 deletions packages/react-cosmos/src/server/__tests__/export.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ import startExport from '../export';
const mockRootPath = __dirname;
const mockOutputPath = path.join(__dirname, './__fsoutput__/export');

jest.mock('react-cosmos-config', () => () => ({
rootPath: mockRootPath,
outputPath: mockOutputPath,
globalImports: [],
componentPaths: []
jest.mock('react-cosmos-config', () => ({
hasUserCosmosConfig: () => true,
getCosmosConfig: () => ({
rootPath: mockRootPath,
outputPath: mockOutputPath,
globalImports: [],
componentPaths: []
})
}));

jest.mock('webpack', () =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import extendWebpackConfig from '../../extend-webpack-config';

jest.mock('react-cosmos-config', () => () => ({
componentPaths: ['src/components'],
fixturePaths: ['test/fixtures'],
ignore: [],
globalImports: ['./global.css'],
hot: true,
outputPath: '__mock__outputPath',
containerQuerySelector: '__mock__containerQuerySelector'
jest.mock('react-cosmos-config', () => ({
hasUserCosmosConfig: () => true,
getCosmosConfig: () => ({
componentPaths: ['src/components'],
fixturePaths: ['test/fixtures'],
ignore: [],
globalImports: ['./global.css'],
hot: true,
outputPath: '__mock__outputPath',
containerQuerySelector: '__mock__containerQuerySelector'
})
}));

const DefinePlugin = jest.fn();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import extendWebpackConfig from '../../extend-webpack-config';

jest.mock('react-cosmos-config', () => () => ({
componentPaths: ['src/components'],
fixturePaths: ['test/fixtures'],
ignore: [],
globalImports: ['./global.css'],
containerQuerySelector: '__mock__containerQuerySelector'
jest.mock('react-cosmos-config', () => ({
hasUserCosmosConfig: () => true,
getCosmosConfig: () => ({
componentPaths: ['src/components'],
fixturePaths: ['test/fixtures'],
ignore: [],
globalImports: ['./global.css'],
containerQuerySelector: '__mock__containerQuerySelector'
})
}));

const DefinePlugin = jest.fn();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import webpack from 'webpack';
import extendWebpackConfig from '../../extend-webpack-config';

jest.mock('react-cosmos-config', () => () => ({
componentPaths: ['src/components'],
fixturePaths: ['test/fixtures'],
ignore: [],
globalImports: ['./global.css'],
hot: true
jest.mock('react-cosmos-config', () => ({
hasUserCosmosConfig: () => true,
getCosmosConfig: () => ({
componentPaths: ['src/components'],
fixturePaths: ['test/fixtures'],
ignore: [],
globalImports: ['./global.css'],
hot: true
})
}));

const getConfig = () =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import extendWebpackConfig from '../../extend-webpack-config';

jest.mock('react-cosmos-config', () => () => ({
componentPaths: ['src/components'],
fixturePaths: ['test/fixtures'],
ignore: [],
globalImports: ['./global.css'],
hot: true
jest.mock('react-cosmos-config', () => ({
hasUserCosmosConfig: () => true,
getCosmosConfig: () => ({
componentPaths: ['src/components'],
fixturePaths: ['test/fixtures'],
ignore: [],
globalImports: ['./global.css'],
hot: true
})
}));

const DefinePlugin = jest.fn();
Expand Down
Loading

0 comments on commit 8a5c65c

Please sign in to comment.