Skip to content

Commit b879792

Browse files
built in serve command
1 parent 61dd7eb commit b879792

File tree

5 files changed

+112
-7
lines changed

5 files changed

+112
-7
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"clean:deps": "rimraf **/node_modules/**",
1919
"lint": "ls-lint && eslint \"*.js\" \"./packages/**/**/*.js\" \"./test/*.js\" \"./www/**/**/*.js\"",
2020
"build": "node . build",
21-
"serve": "yarn build && cd ./public && npx http-server .",
21+
"serve": "node . serve",
2222
"develop": "node . develop",
2323
"test": "export BROWSERSLIST_IGNORE_OLD_DATA=true && nyc mocha",
2424
"test:tdd": "yarn test --watch"

packages/cli/src/commands/build.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const { execSync } = require('child_process');
22
const fs = require('fs');
33
const generateCompilation = require('../lifecycles/compile');
44
const serializeBuild = require('../lifecycles/serialize');
5-
const { server } = require('../lifecycles/serve');
5+
const { devServer } = require('../lifecycles/serve');
66

77
module.exports = runProductionBuild = async () => {
88

@@ -13,7 +13,7 @@ module.exports = runProductionBuild = async () => {
1313
const port = compilation.config.devServer.port;
1414
const outputDir = compilation.context.outputDir;
1515

16-
await server.listen(port);
16+
devServer(compilation).listen(port);
1717

1818
if (!fs.existsSync(outputDir)) {
1919
fs.mkdirSync(outputDir);

packages/cli/src/commands/serve.js

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
const generateCompilation = require('../lifecycles/compile');
2+
const { prodServer } = require('../lifecycles/serve');
3+
4+
module.exports = runProdServer = async () => {
5+
6+
return new Promise(async (resolve, reject) => {
7+
8+
try {
9+
const compilation = await generateCompilation();
10+
const port = 8080;
11+
12+
prodServer(compilation).listen(port, () => {
13+
console.info(`Started production test server at localhost:${port}`);
14+
});
15+
} catch (err) {
16+
reject(err);
17+
}
18+
19+
});
20+
};

packages/cli/src/index.js

+17-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ process.setMaxListeners(0);
1010
const program = require('commander');
1111
const runProductionBuild = require('./commands/build');
1212
const runDevServer = require('./commands/develop');
13+
const runProdServer = require('./commands/serve');
1314
// const ejectConfigFiles = require('./tasks/eject');
1415
const greenwoodPackageJson = require('../package.json');
1516

@@ -36,13 +37,21 @@ program
3637
.action((cmd) => {
3738
command = cmd._name;
3839
});
40+
3941
program
4042
.command('develop')
4143
.description('Start a local development server.')
4244
.action((cmd) => {
4345
command = cmd._name;
4446
});
4547

48+
program
49+
.command('serve')
50+
.description('View a production build locally with a basic web server.')
51+
.action((cmd) => {
52+
command = cmd._name;
53+
});
54+
4655
program
4756
.command('eject')
4857
.option('-a, --all', 'eject all configurations including babel, postcss, browserslistrc')
@@ -66,13 +75,20 @@ const run = async() => {
6675

6776
switch (command) {
6877

69-
case 'build':
78+
case 'build':
7079
await runProductionBuild();
7180

7281
break;
7382
case 'develop':
7483
await runDevServer();
7584

85+
break;
86+
case 'serve':
87+
process.env.__GWD_COMMAND__ = 'build';
88+
89+
await runProductionBuild();
90+
await runProdServer();
91+
7692
break;
7793
// TODO
7894
// case 'eject'

packages/cli/src/lifecycles/serve.js

+72-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ function getDevServer(compilation) {
1919
const { userWorkspace, scratchDir } = context;
2020
const app = new Koa();
2121

22-
// TODO export this an async function
22+
// TODO use url.endsWith!!
2323
app.use(async ctx => {
2424
// console.debug('URL', ctx.request.url);
2525

@@ -32,7 +32,6 @@ function getDevServer(compilation) {
3232

3333
// make sure this only happens for "pages", nor partials or fixtures, templates, et)
3434
if (ctx.request.url.indexOf('.html') >= 0) {
35-
// TODO get this stuff from compilation
3635
let title = config.title;
3736
const metaOutletContent = config.meta.map(item => {
3837
let metaHtml = '';
@@ -250,6 +249,7 @@ function getDevServer(compilation) {
250249
}
251250
}
252251

252+
// TODO break up into distinct font / icons / svg handlers, not related to assets/
253253
if (ctx.request.url.indexOf('assets/') >= 0 && ctx.request.url.indexOf('.css') < 0) {
254254
const assetPath = path.join(userWorkspace, ctx.request.url);
255255
const ext = path.extname(assetPath);
@@ -285,6 +285,75 @@ function getDevServer(compilation) {
285285
return app;
286286
}
287287

288+
function getProdServer(compilation) {
289+
const app = new Koa();
290+
291+
app.use(async ctx => {
292+
// console.debug('URL', ctx.request.url);
293+
const { outputDir } = compilation.context;
294+
295+
if (ctx.request.url.endsWith('/')) {
296+
ctx.redirect(`http://localhost:8080${ctx.request.url}index.html`);
297+
}
298+
299+
if (ctx.request.url.endsWith('.html')) {
300+
const contents = await fsp.readFile(path.join(outputDir, ctx.request.url), 'utf-8');
301+
302+
ctx.set('Content-Type', 'text/html');
303+
ctx.body = contents;
304+
}
305+
306+
if (ctx.request.url.endsWith('.js')) {
307+
const contents = await fsp.readFile(path.join(outputDir, ctx.request.url), 'utf-8');
308+
309+
ctx.set('Content-Type', 'text/javascript');
310+
ctx.body = contents;
311+
}
312+
313+
if (ctx.request.url.endsWith('.css')) {
314+
const contents = await fsp.readFile(path.join(outputDir, ctx.request.url), 'utf-8');
315+
316+
ctx.set('Content-Type', 'text/css');
317+
ctx.body = contents;
318+
}
319+
320+
// TODO break up into distinct font / icons / svg handlers, decouple from to assets/
321+
if (ctx.request.url.indexOf('assets/')) {
322+
const assetPath = path.join(outputDir, ctx.request.url);
323+
const ext = path.extname(assetPath);
324+
const type = ext === '.svg'
325+
? `${ext.replace('.', '')}+xml`
326+
: ext.replace('.', '');
327+
328+
if (['.jpg', '.png', '.gif', '.svg'].includes(ext)) {
329+
ctx.set('Content-Type', `image/${type}`);
330+
331+
if (ext === '.svg') {
332+
ctx.body = await fsp.readFile(assetPath, 'utf-8');
333+
} else {
334+
ctx.body = await fsp.readFile(assetPath);
335+
}
336+
} else if (['.woff2', '.woff', '.ttf'].includes(ext)) {
337+
ctx.set('Content-Type', `font/${type}`);
338+
ctx.body = await fsp.readFile(assetPath);
339+
} else if (['.ico'].includes(ext)) {
340+
ctx.set('Content-Type', `image/x-icon`);
341+
ctx.body = await fsp.readFile(assetPath);
342+
}
343+
}
344+
345+
if (ctx.request.url.endsWith('.json')) {
346+
const contents = await fsp.readFile(path.join(outputDir, 'graph.json'), 'utf-8');
347+
348+
ctx.set('Content-Type', 'application/json');
349+
ctx.body = JSON.parse(contents);
350+
}
351+
});
352+
353+
return app;
354+
}
355+
288356
module.exports = {
289-
devServer: getDevServer
357+
devServer: getDevServer,
358+
prodServer: getProdServer
290359
};

0 commit comments

Comments
 (0)