Skip to content

Commit

Permalink
fix(watch): Add workaround for the webpack watch issue
Browse files Browse the repository at this point in the history
  • Loading branch information
edeustace committed Mar 29, 2017
1 parent a9d01e3 commit b29aeff
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 13 deletions.
7 changes: 4 additions & 3 deletions src/apps/info/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import * as webpackMiddleware from 'webpack-dev-middleware';

import { App, Servable, ServeOpts, ServeResult } from '../types';
import AppServer, { linkCompilerToServer } from '../../server';
import { existsSync, readFileSync, readJsonSync, writeFileSync } from 'fs-extra';
import { existsSync, readFileSync, readJsonSync } from 'fs-extra';
import { join, resolve } from 'path';
import { writeConfig, writeEntryJs } from '../../code-gen';

import Install from '../../install';
import { JsonConfig } from '../../question/config';
import { SupportConfig } from './../../framework-support';
import { buildLogger } from 'log-factory';
import entryJs from './entry';
import { webpackConfig } from '../common';
import { writeConfig } from '../../code-gen/webpack-write-config';

const logger = buildLogger();
const templatePath = join(__dirname, 'views/index.pug');
Expand Down Expand Up @@ -75,7 +75,8 @@ export default class InfoApp implements App, Servable {
mappings,
AppServer.SOCK_PREFIX);

writeFileSync(join(this.installer.dir, InfoApp.ENTRY), js, 'utf8');
await writeEntryJs(join(this.installer.dir, InfoApp.ENTRY), js);

const config = webpackConfig(this.installer, this.support, InfoApp.ENTRY, InfoApp.BUNDLE);

const cssRule = config.module.rules.find((r) => {
Expand Down
4 changes: 2 additions & 2 deletions src/apps/item/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { SupportConfig } from '../../framework-support';
import { buildLogger } from 'log-factory';
import entryJs from './entry';
import { webpackConfig } from '../common';
import { writeConfig } from '../../code-gen/webpack-write-config';
import { writeConfig, writeEntryJs } from '../../code-gen';
import { writeFileSync } from 'fs-extra';

const logger = buildLogger();
Expand Down Expand Up @@ -63,7 +63,7 @@ export default class ItemApp implements App, Servable {
mappings.controllers,
AppServer.SOCK_PREFIX);

writeFileSync(join(this.installer.dirs.root, ItemApp.ENTRY), js, 'utf8');
await writeEntryJs(join(this.installer.dirs.root, ItemApp.ENTRY), js);

logger.info('add sourceMaps? ', opts.sourceMaps);

Expand Down
38 changes: 38 additions & 0 deletions src/code-gen/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { build as buildWebpack } from './webpack-builder';
import { pascalCase } from 'change-case';
import { writeConfig } from './webpack-write-config';
export { buildWebpack, writeConfig }
import { writeFile, utimes } from 'fs-extra';

export interface Declaration {
key: string;
Expand All @@ -24,3 +25,40 @@ export class ElementDeclaration implements Declaration {
}
}

function writeFilePromise(path: string, content: string): Promise<{}> {
return new Promise((resolve, reject) => {
writeFile(path, content, 'utf8', (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}

function adjustUTimes(path: string, ageInSeconds: number) {
return new Promise((resolve, reject) => {
const now = Date.now() / 1000;
const then = now - ageInSeconds;
utimes(path, then, then, (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}

/**
* Write the entry js - backdate the file's mtime to avoid the following issue:
* https://github.com/webpack/watchpack/issues/25
*
* @param path
* @param js
*/
export function writeEntryJs(path: string, js: string): Promise<{}> {
return writeFilePromise(path, js)
.then(() => adjustUTimes(path, 10));
}
5 changes: 2 additions & 3 deletions src/server/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ export function linkCompilerToServer(name: string,
* TODO: This shouldn't be necessary but something is causing webpack to recompile repeatedly.
* For now we debounce the done handler.
*/
compiler.plugin('compile', _.debounce(onCompile, 450, { leading: true, trailing: false }));
const debouncedOnDone = _.debounce(onDone, 450, { leading: false, trailing: true });
compiler.plugin('done', debouncedOnDone);
compiler.plugin('compile', onCompile);
compiler.plugin('done', onDone);
}
10 changes: 5 additions & 5 deletions test/unit/apps/info/index-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ describe('info app', () => {
createServer: stub().returns({})
},
'fs-extra': {
writeFileSync: stub(),
readFileSync: stub().returns(''),
readJsonSync: stub().returns({
name: 'name',
Expand All @@ -58,8 +57,9 @@ describe('info app', () => {
}
})
},
'../../code-gen/webpack-write-config': {
writeConfig: stub()
'../../code-gen': {
writeConfig: stub(),
writeEntryJs: stub().returns(Promise.resolve())
}
}

Expand Down Expand Up @@ -115,8 +115,8 @@ describe('info app', () => {
assert.calledWith(instance.installer.install, undefined);
});

it('calls writeFileSync', () => {
assert.calledWith(deps['fs-extra'].writeFileSync, '.pie/info.entry.js', match.string, 'utf8');
it('calls writeEntryJs', () => {
assert.calledWith(deps['../../code-gen'].writeEntryJs, '.pie/info.entry.js', match.string);
});

it('calls webpackConfig', () => {
Expand Down
182 changes: 182 additions & 0 deletions test/unit/apps/item/index-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import { assert, match, spy, stub } from 'sinon';

import { Base } from '../helper';
import { expect } from 'chai';
import path from 'path';
import proxyquire from 'proxyquire';

const ROOT = '../../../../lib';

describe('item app', () => {
let ItemApp, instance, mod, args, deps, config, supportConfig, middlewareInstance, routerInstance, express, compiler;

beforeEach(() => {

routerInstance = {
use: stub(),
get: stub()
}

middlewareInstance = {
waitUntilValid: stub()
}

compiler = {
plugin: stub()
}

express = stub().returns(routerInstance);
express.Router = stub().returns(routerInstance);

deps = {
'express': express,
'http': {
createServer: stub().returns({})
},
'fs-extra': {
readFileSync: stub().returns(''),
readJsonSync: stub().returns({
name: 'name',
version: 'version',
repository: {
url: 'url'
}
})
},
webpack: stub().returns(compiler),
'webpack-dev-middleware': stub().returns(middlewareInstance),
'../../server': {
default: stub()
},
'../common': {
webpackConfig: stub().returns({
module: {
rules: [
{ test: /\.css$/, use: 'css-loader' }
]
}
})
},
'../../code-gen': {
writeConfig: stub(),
writeEntryJs: stub().returns(Promise.resolve())
}
}

mod = proxyquire(`${ROOT}/apps/item`, deps);

ItemApp = mod.default;

args = {};

config = {
dir: 'dir',
declarations: [],
pieModels: stub(),
elementModels: stub()
};

supportConfig = {
externals: {
js: [],
css: []
}
};

instance = new ItemApp(args, config, supportConfig);
});

describe('constructor', () => {
it('constructs', () => {
expect(instance).not.to.be.undefined;
});
});


describe('server', () => {

beforeEach(() => {

instance.router = stub();

instance.installer = {
dir: '.pie',
dirs: {
root: 'dir',
configure: '.configure',
controllers: './controllers'
},
install: stub().returns(Promise.resolve({ controllers: [], configure: [] }))
}
return instance.server({});
});

it('calls intaller.install', () => {
assert.calledWith(instance.installer.install, undefined);
});

it('calls writeEntryJs', () => {
assert.calledWith(deps['../../code-gen'].writeEntryJs, 'dir/item.entry.js', match.string);
});

it('calls webpackConfig', () => {
assert.calledWith(deps['../common'].webpackConfig, match.object, match.object, 'item.entry.js', 'item.bundle.js');
});

it('calls webpack', () => {
assert.calledWith(deps['webpack'], match.object);
});

it('calls router', () => {
assert.calledWith(instance.router, compiler);
});
});

describe('router', () => {
beforeEach(() => {
instance.router({});
});

it('calls express.Router', () => {
assert.called(deps['express'].Router);
});

it('calls webpackMiddleware', () => {
assert.calledWith(deps['webpack-dev-middleware'], match.object, {
noInfo: true,
quiet: true,
publicPath: '/'
});
});

it('calls router.use', () => {
assert.calledWith(routerInstance.use, middlewareInstance);
});

describe('GET /', () => {
let handler, res;
beforeEach(() => {

res = {}
res.set = stub().returns(res);
res.status = stub().returns(res);
res.send = stub();

instance.template = stub();
instance.installer = {
installedPies: []
}
routerInstance.get = spy(function (path, h) {
handler = h;
});
instance.router({});
handler({}, res);
});

it('calls template', () => {
assert.called(instance.template);
});

});
});
});

0 comments on commit b29aeff

Please sign in to comment.