Skip to content

Commit

Permalink
feat: Update cosmos.modules file on fixture file changes (#735)
Browse files Browse the repository at this point in the history
  • Loading branch information
ovidiuch committed Jun 30, 2018
1 parent 385bc4b commit 3f56d45
Show file tree
Hide file tree
Showing 17 changed files with 241 additions and 49 deletions.
3 changes: 3 additions & 0 deletions .travis.yml
@@ -1,8 +1,11 @@
language: node_js
sudo: required
node_js:
- 9
- 8
- 6
before_install:
- sudo sysctl fs.inotify.max_user_watches=524288
- curl -o- -L https://yarnpkg.com/install.sh | bash
- export PATH=$HOME/.yarn/bin:$PATH
script:
Expand Down
3 changes: 2 additions & 1 deletion TODO.md
Expand Up @@ -13,7 +13,7 @@
- [x] Create RN server cmd
- [x] Create sockets server (forwarding messages between clients)
- [x] Generate user modules file
- [ ] Update user modules file on changes
- [x] Update cosmos.modules file on fixture file changes
- [x] Add loaderOpts to cosmos.modules
- [ ] Onboarding
- [ ] Generate Cosmos config
Expand All @@ -32,3 +32,4 @@
- [x] Test socket transport
- [ ] Check if existing proxies are compatible
- [x] Create React Native App example
- [ ] Add (experimental) README section
2 changes: 1 addition & 1 deletion examples/create-react-native-app/link-workspaces.js
@@ -1 +1 @@
require('crna-make-symlinks-for-yarn-workspaces')(__dirname);
require('@skidding/crna-make-symlinks-for-yarn-workspaces')(__dirname);
4 changes: 2 additions & 2 deletions examples/create-react-native-app/package.json
Expand Up @@ -17,8 +17,8 @@
"react-native": "^0.55.4"
},
"devDependencies": {
"crna-make-symlinks-for-yarn-workspaces": "^1.0.1",
"metro-bundler-config-yarn-workspaces": "^1.0.3",
"@skidding/crna-make-symlinks-for-yarn-workspaces": "^1.0.1",
"@skidding/metro-bundler-config-yarn-workspaces": "^1.0.5",
"react-native-scripts": "^1.14.0"
},
"xo": false,
Expand Down
2 changes: 1 addition & 1 deletion examples/create-react-native-app/rn-cli.config.js
@@ -1,5 +1,5 @@
const { resolve } = require('path');
const getConfig = require('metro-bundler-config-yarn-workspaces');
const getConfig = require('@skidding/metro-bundler-config-yarn-workspaces');

module.exports = getConfig(__dirname, {
nodeModules: resolve(__dirname, '../../node_modules'),
Expand Down
2 changes: 1 addition & 1 deletion examples/local-state/package.json
Expand Up @@ -10,7 +10,7 @@
"test": "node ../../node_modules/jest-cli/bin/jest.js --watch --config ../../jest.config.json"
},
"dependencies": {
"async-until": "^1.0.1",
"async-until": "^1.1.0",
"react-cosmos": "^4.5.0",
"react-cosmos-test": "^4.5.0"
},
Expand Down
2 changes: 1 addition & 1 deletion jest.config.json
Expand Up @@ -10,7 +10,7 @@
"/__fsmocks__/",
"/_shared"
],
"watchPathIgnorePatterns": ["/__fsoutput__/"],
"watchPathIgnorePatterns": ["/__fsoutput__/", "jestnowatch"],
"transformIgnorePatterns": [
"/node_modules/(?!(react-cosmos.+|react-native))"
],
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -37,7 +37,7 @@
"devDependencies": {
"after-pending-promises": "^0.0.1",
"after-pending-timers": "^0.0.1",
"async-until": "^1.0.1",
"async-until": "^1.1.0",
"babel-cli": "^6.26.0",
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.3",
Expand Down
2 changes: 1 addition & 1 deletion packages/react-cosmos-apollo-proxy/package.json
Expand Up @@ -16,7 +16,7 @@
},
"xo": false,
"devDependencies": {
"async-until": "^1.0.1",
"async-until": "^1.1.0",
"fetch-mock": "^5.12.2",
"graphql-tag": "^2.9.2"
}
Expand Down
2 changes: 1 addition & 1 deletion packages/react-cosmos-loader/package.json
Expand Up @@ -6,7 +6,7 @@
"license": "MIT",
"main": "index.js",
"dependencies": {
"async-until": "^1.0.1",
"async-until": "^1.1.0",
"deep-equal": "^1.0.1",
"lodash.merge": "^4.6.1",
"prop-types": "^15.6.1",
Expand Down
Expand Up @@ -280,6 +280,9 @@ export default class ComponentPlayground extends Component<Props, State> {
const { status } = await fetch(loaderUri, {
credentials: 'same-origin'
});
// WARN: At this point the component could be unmounted and we'll get the
// following React warning:
// > Can't call setState (or forceUpdate) on an unmounted component.
if (status === 200) {
// Wait until all session settings are read before rendering
this.restoreUserSettings(() => {
Expand Down
3 changes: 2 additions & 1 deletion packages/react-cosmos/package.json
Expand Up @@ -6,12 +6,13 @@
"license": "MIT",
"dependencies": {
"babel-polyfill": "^6.26.0",
"chokidar": "^2.0.4",
"express": "^4.16.3",
"fs-extra": "^6.0.1",
"glob": "^7.1.1",
"http-proxy-middleware": "^0.18.0",
"import-from": "^2.1.0",
"lodash.omit": "^4.5.0",
"lodash": "^4.17.10",
"object.values": "^1.0.4",
"react-cosmos-config": "^4.5.0",
"react-cosmos-loader": "^4.4.0",
Expand Down
@@ -0,0 +1,65 @@
/**
* @flow
* @jest-environment node
*/

import { join } from 'path';
import { readFile, copy, remove } from 'fs-extra';
import until from 'async-until';
import {
defaultFileMatch as mockFileMatch,
defaultFileMatchIgnore as mockFileMatchIgnore,
defaultExclude as mockExclude
} from 'react-cosmos-shared/server';
import { startServer } from '../start';

const mockRootPath = join(__dirname, '__fsmocks__');
const mockProxiesPath = join(mockRootPath, 'cosmos.proxies');
const mockNewFixturePath = join(mockRootPath, 'jestnowatch.fixture.js');
const mockModulesPath = join(__dirname, '__fsoutput__/cosmos.modules.js');

jest.mock('react-cosmos-config', () => ({
getCosmosConfig: () => ({
rootPath: mockRootPath,
port: 10002,
hostname: null,
publicUrl: '/',
fileMatch: mockFileMatch,
fileMatchIgnore: mockFileMatchIgnore,
exclude: mockExclude,
proxiesPath: mockProxiesPath,
modulesPath: mockModulesPath
})
}));

let stopServer;

beforeEach(async () => {
jest.clearAllMocks();
stopServer = await startServer();
});

afterEach(async () => {
await stopServer();
await remove(mockModulesPath);
await remove(mockNewFixturePath);
});

it('re-generates modules file on new fixture file ', async () => {
expect((await getFixtureFilesFromModules()).length).toBe(1);

await copy(join(mockRootPath, 'MyComponent.fixture.js'), mockNewFixturePath);

// Wait for fs event to be picked up
await until(async () => (await getFixtureFilesFromModules()).length === 2, {
loopDelay: 200,
timeout: 2000,
failMsg: 'cosmos.modules file has not been updated'
});
});

async function getFixtureFilesFromModules() {
const output = await readFile(mockModulesPath, 'utf8');

return JSON.parse(output.match(/fixtureFiles: (.+?),\n/)[1]);
}
16 changes: 6 additions & 10 deletions packages/react-cosmos/src/server/native/__tests__/start.js
Expand Up @@ -4,9 +4,8 @@
*/

import { join } from 'path';
import { readFile, unlink } from 'fs';
import { readFile, remove } from 'fs-extra';
import request from 'request-promise-native';
import promisify from 'util.promisify';
import {
defaultFileMatch as mockFileMatch,
defaultFileMatchIgnore as mockFileMatchIgnore,
Expand All @@ -15,9 +14,6 @@ import {
import io from 'socket.io-client';
import { startServer } from '../start';

const readFileAsync = promisify(readFile);
const unlinkAsync = promisify(unlink);

const mockRootPath = join(__dirname, '__fsmocks__');
const mockProxiesPath = join(mockRootPath, 'cosmos.proxies');
const mockModulesPath = join(__dirname, '__fsoutput__/cosmos.modules.js');
Expand Down Expand Up @@ -45,12 +41,12 @@ beforeEach(async () => {

afterEach(async () => {
await stopServer();
await unlinkAsync(mockModulesPath);
await remove(mockModulesPath);
});

it('serves index.html on / route with playgrounds opts included', async () => {
const res = await request('http://127.0.0.1:10001/');
const source = await readFileAsync(
const source = await readFile(
require.resolve('../../shared/static/index.html'),
'utf8'
);
Expand All @@ -66,7 +62,7 @@ it('serves index.html on / route with playgrounds opts included', async () => {

it('serves playground js on /_playground.js route', async () => {
const res = await request('http://127.0.0.1:10001/_playground.js');
const source = await readFileAsync(
const source = await readFile(
require.resolve('react-cosmos-playground'),
'utf8'
);
Expand All @@ -76,7 +72,7 @@ it('serves playground js on /_playground.js route', async () => {

it('serves favicon.ico on /_cosmos.ico route', async () => {
const res = await request('http://127.0.0.1:10001/_cosmos.ico');
const source = await readFileAsync(
const source = await readFile(
require.resolve('../../shared/static/favicon.ico'),
'utf8'
);
Expand All @@ -101,7 +97,7 @@ it('broadcasts events to between clients', async () => {
});

it('generates modules file', async () => {
const output = await readFileAsync(mockModulesPath, 'utf8');
const output = await readFile(mockModulesPath, 'utf8');

const fixturePath = join(mockRootPath, 'MyComponent.fixture.js');
const componentPath = join(mockRootPath, 'MyComponent.js');
Expand Down
32 changes: 32 additions & 0 deletions packages/react-cosmos/src/server/native/start.js
@@ -1,5 +1,7 @@
// @flow

import chokidar from 'chokidar';
import { debounce } from 'lodash';
import { getCosmosConfig } from 'react-cosmos-config';
import {
createServerApp,
Expand Down Expand Up @@ -28,7 +30,10 @@ export async function startServer() {
const closeSockets = attachSockets(server);
await startServer();

const watcher = await startFixtureFileWatcher(cosmosConfig);

return async () => {
watcher.close();
await closeSockets();
await stopServer();
};
Expand All @@ -40,3 +45,30 @@ function getPlaygroundOpts({ rootPath }) {
projectKey: rootPath
};
}

async function startFixtureFileWatcher(cosmosConfig) {
const { rootPath, fileMatch } = cosmosConfig;

return new Promise(resolve => {
const watcher = chokidar
.watch(fileMatch, {
ignored: getNormalizedIgnores(cosmosConfig),
ignoreInitial: true,
cwd: rootPath
})
.on('ready', () => resolve(watcher))
.on(
'all',
debounce(() => {
// Rebuild cosmos.modules file on fixture file changes
generateModulesFile(cosmosConfig);
}, 50)
);
});
}

function getNormalizedIgnores({ fileMatchIgnore, exclude }) {
return Array.isArray(exclude)
? [fileMatchIgnore, ...exclude]
: [fileMatchIgnore, exclude];
}
@@ -1,7 +1,7 @@
// @flow

import { resolve, join } from 'path';
import omit from 'lodash.omit';
import { omit } from 'lodash';
import { getCosmosConfig } from 'react-cosmos-config';

import type { Config } from 'react-cosmos-flow/config';
Expand Down

0 comments on commit 3f56d45

Please sign in to comment.