gatsby
Version:
Blazing fast modern site generator for React
290 lines (285 loc) • 11 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _path = _interopRequireDefault(require("path"));
var _betterOpn = _interopRequireDefault(require("better-opn"));
var _fsExtra = _interopRequireDefault(require("fs-extra"));
var _compression = _interopRequireDefault(require("compression"));
var _express = _interopRequireDefault(require("express"));
var _chalk = _interopRequireDefault(require("chalk"));
var _reachRouter = require("@gatsbyjs/reach-router");
var _signalExit = _interopRequireDefault(require("signal-exit"));
var _reporter = _interopRequireDefault(require("gatsby-cli/lib/reporter"));
var _gatsbyTelemetry = _interopRequireDefault(require("gatsby-telemetry"));
var _detectPortInUseAndPrompt = require("../utils/detect-port-in-use-and-prompt");
var _getConfigFile = require("../bootstrap/get-config-file");
var _preferDefault = require("../bootstrap/prefer-default");
var _prepareUrls = require("../utils/prepare-urls");
var _pageData = require("../utils/page-data");
var _tracer = require("../utils/tracer");
var _expressMiddlewares = require("../utils/express-middlewares");
var _datastore = require("../datastore");
var _middleware = require("../internal-plugins/functions/middleware");
var _proxy = require("../internal-plugins/partytown/proxy");
var _path2 = require("gatsby-core-utils/path");
(0, _signalExit.default)(() => {
_gatsbyTelemetry.default.trackCli(`SERVE_STOP`);
});
const readMatchPaths = async program => {
const filePath = _path.default.join(program.directory, `.cache`, `match-paths.json`);
let rawJSON = `[]`;
try {
rawJSON = await _fsExtra.default.readFile(filePath, `utf8`);
} catch (error) {
_reporter.default.warn(error);
_reporter.default.warn(`Could not read ${_chalk.default.bold(`match-paths.json`)} from the .cache directory`);
_reporter.default.warn(`Client-side routing will not work correctly. Maybe you need to re-run ${_chalk.default.bold(`gatsby build`)}?`);
}
return JSON.parse(rawJSON);
};
const matchPathRouter = (matchPaths, options) => (req, res, next) => {
const {
url
} = req;
if (req.accepts(`html`)) {
const matchPath = matchPaths.find(({
matchPath
}) => (0, _reachRouter.match)(matchPath, url) !== null);
if (matchPath) {
return res.sendFile(_path.default.join(matchPath.path, `index.html`), options, err => {
if (err) {
next();
}
});
}
}
return next();
};
module.exports = async program => {
_gatsbyTelemetry.default.trackCli(`SERVE_START`);
_gatsbyTelemetry.default.startBackgroundUpdate();
await (0, _tracer.initTracer)(process.env.GATSBY_OPEN_TRACING_CONFIG_FILE || program.openTracingConfigFile);
let {
prefixPaths,
port,
open,
host
} = program;
port = typeof port === `string` ? parseInt(port, 10) : port;
const {
configModule
} = await (0, _getConfigFile.getConfigFile)(program.directory, `gatsby-config`);
const config = (0, _preferDefault.preferDefault)(configModule);
const {
pathPrefix: configPathPrefix,
trailingSlash
} = config || {};
const pathPrefix = prefixPaths && configPathPrefix ? configPathPrefix : `/`;
const root = _path.default.join(program.directory, `public`);
const app = (0, _express.default)();
// Proxy gatsby-script using off-main-thread strategy
const {
partytownProxiedURLs = []
} = config || {};
app.use(_proxy.thirdPartyProxyPath, (0, _proxy.partytownProxy)(partytownProxiedURLs));
// eslint-disable-next-line new-cap
const router = _express.default.Router();
app.use(_gatsbyTelemetry.default.expressMiddleware(`SERVE`));
router.use((0, _compression.default)());
router.use((0, _expressMiddlewares.configureTrailingSlash)(() => ({
pages: {
get(pathName) {
return (0, _datastore.getDataStore)().getNode(`SitePage ${pathName}`);
},
values() {
return (0, _datastore.getDataStore)().iterateNodesByType(`SitePage`);
}
}
}), trailingSlash));
router.use(_express.default.static(`public`, {
dotfiles: `allow`
}));
const compiledFunctionsDir = _path.default.join(program.directory, `.cache`, `functions`);
let functions = [];
try {
functions = JSON.parse(_fsExtra.default.readFileSync(_path.default.join(compiledFunctionsDir, `manifest.json`), `utf-8`));
} catch (e) {
// ignore
}
if (functions) {
const functionMiddlewaresInstances = (0, _middleware.functionMiddlewares)({
getFunctions() {
return functions;
}
});
router.use(`/api/*`, ...functionMiddlewaresInstances);
// TODO(v6) remove handler from app and only keep it on router (router is setup on pathPrefix, while app is always root)
app.use(`/api/*`, ...functionMiddlewaresInstances);
}
// Handle SSR & DSG Pages
let graphqlEnginePath;
let pageSSRModule;
try {
graphqlEnginePath = require.resolve(_path.default.posix.join((0, _path2.slash)(program.directory), `.cache`, `query-engine`));
pageSSRModule = require.resolve(_path.default.posix.join((0, _path2.slash)(program.directory), `.cache`, `page-ssr`));
} catch (error) {
// TODO: Handle case of engine not being generated
}
if (graphqlEnginePath && pageSSRModule) {
try {
const {
GraphQLEngine
} = require(graphqlEnginePath);
const {
getData,
renderPageData,
renderHTML,
findEnginePageByPath
} = require(pageSSRModule);
const graphqlEngine = new GraphQLEngine({
dbPath: _path.default.posix.join((0, _path2.slash)(program.directory), `.cache`, `data`, `datastore`)
});
router.get(`/page-data/:pagePath(*)/page-data.json`, async (req, res, next) => {
const requestedPagePath = req.params.pagePath;
if (!requestedPagePath) {
return void next();
}
const potentialPagePath = (0, _pageData.reverseFixedPagePath)(requestedPagePath);
const page = findEnginePageByPath(potentialPagePath);
if (page && (page.mode === `DSG` || page.mode === `SSR`)) {
const requestActivity = _reporter.default.phantomActivity(`request for "${req.path}"`);
requestActivity.start();
try {
const spanContext = requestActivity.span.context();
const data = await getData({
pathName: req.path,
graphqlEngine,
req,
spanContext
});
const results = await renderPageData({
data,
spanContext
});
if (data.serverDataHeaders) {
for (const [name, value] of Object.entries(data.serverDataHeaders)) {
res.setHeader(name, value);
}
}
if (page.mode === `SSR` && data.serverDataStatus) {
return void res.status(data.serverDataStatus).send(results);
} else {
return void res.send(results);
}
} catch (e) {
_reporter.default.error(`Generating page-data for "${requestedPagePath}" / "${potentialPagePath}" failed.`, e);
return res.status(500).contentType(`text/plain`).send(`Internal server error.`);
} finally {
requestActivity.end();
}
}
return void next();
});
router.use(async (req, res, next) => {
if (req.accepts(`html`)) {
const potentialPagePath = req.path;
const page = findEnginePageByPath(potentialPagePath);
if (page && (page.mode === `DSG` || page.mode === `SSR`)) {
const requestActivity = _reporter.default.phantomActivity(`request for "${req.path}"`);
requestActivity.start();
try {
const spanContext = requestActivity.span.context();
const data = await getData({
pathName: potentialPagePath,
graphqlEngine,
req,
spanContext
});
const results = await renderHTML({
data,
spanContext
});
if (data.serverDataHeaders) {
for (const [name, value] of Object.entries(data.serverDataHeaders)) {
res.setHeader(name, value);
}
}
if (page.mode === `SSR` && data.serverDataStatus) {
return void res.status(data.serverDataStatus).send(results);
} else {
return void res.send(results);
}
} catch (e) {
_reporter.default.error(`Rendering html for "${potentialPagePath}" failed.`, e);
return res.status(500).sendFile(`500.html`, {
root
}, err => {
if (err) {
res.contentType(`text/plain`).send(`Internal server error.`);
}
});
} finally {
requestActivity.end();
}
}
}
return next();
});
} catch (error) {
_reporter.default.panic({
id: `98051`,
error,
context: {}
});
}
}
const matchPaths = await readMatchPaths(program);
router.use(matchPathRouter(matchPaths, {
root
}));
// TODO: Remove/merge with above same block
router.use((req, res, next) => {
if (req.accepts(`html`)) {
return res.status(404).sendFile(`404.html`, {
root
});
}
return next();
});
app.use(function (_, res, next) {
res.header(`Access-Control-Allow-Origin`, `*`);
res.header(`Access-Control-Allow-Headers`, `Origin, X-Requested-With, Content-Type, Accept`);
next();
});
app.use(pathPrefix, router);
function printInstructions(appName, urls) {
console.log();
console.log(`You can now view ${_chalk.default.bold(appName)} in the browser.`);
console.log();
if (urls.lanUrlForTerminal) {
console.log(` ${_chalk.default.bold(`Local:`)} ${urls.localUrlForTerminal}`);
console.log(` ${_chalk.default.bold(`On Your Network:`)} ${urls.lanUrlForTerminal}`);
} else {
console.log(` ${urls.localUrlForTerminal}`);
}
}
const startListening = () => {
app.listen(port, host, () => {
const urls = (0, _prepareUrls.prepareUrls)(program.ssl ? `https` : `http`, program.host, port);
printInstructions(program.sitePackageJson.name || `(Unnamed package)`, urls);
if (open) {
_reporter.default.info(`Opening browser...`);
Promise.resolve((0, _betterOpn.default)(urls.localUrlForBrowser)).catch(() => _reporter.default.warn(`Browser not opened because no browser was found`));
}
});
};
try {
port = await (0, _detectPortInUseAndPrompt.detectPortInUseAndPrompt)(port, program.host);
startListening();
} catch (e) {
if (e.message === `USER_REJECTED`) {
return;
}
throw e;
}
};
//# sourceMappingURL=serve.js.map