2020-06-18 01:44:34 +00:00
|
|
|
// @flow
|
|
|
|
const express = require("express");
|
|
|
|
/*::
|
|
|
|
import type {
|
|
|
|
$Application as ExpressApp,
|
|
|
|
$Response as ExpressResponse,
|
|
|
|
} from "express";
|
|
|
|
*/
|
|
|
|
const path = require("path");
|
Configure fe2 to load from instances (#1856)
This modifies the frontend2 so that we can load real data from cli2
instances, including:
- the root `sourcecred.json` config
- files inside the `config/` directory
- files inside the `output/` directory
This works both for the dev server and the compiled output.
In the case of the dev server, it's now necessary to provide the path to
the cli2 instance you're developing against. You can pass this via the
`--instance` argument, as in `yarn start2 --instance=/path/`, or via
the `$SOURCECRED_DEV_INSTANCE` environment variable (recommended). If
neither is provided, or if that path doesn't look like a valid instance,
an error is thrown.
In the case of the built output, given a valid sc2 instance, you can set
it up via the following:
```
cp -r build2/favicon.png build2/index.html build2/static $INSTANCE
```
Then spin up a simple http server in the $INSTANCE.
You can look at console messages in the frontend to verify that the
instance is working (note it expects to see a discourse config file, not
a GitHub config file as was used in some previous examples).
Test plan:
Setup an example Discourse instance, and then turn on the server via
`yarn start2 --instance=PATH`,
`SOURCECRED_DEV_INSTANCE=path yarn start2`,
and by manually copying in the built outputs using the instructions
above.
In each case, you can load the homepage and check the console to see
that assets loaded successfully.
2020-06-18 01:47:12 +00:00
|
|
|
const fs = require("fs-extra");
|
2020-06-18 01:44:34 +00:00
|
|
|
const webpack = require("webpack");
|
|
|
|
const RemoveBuildDirectoryPlugin = require("./RemoveBuildDirectoryPlugin");
|
|
|
|
const CopyPlugin = require("copy-webpack-plugin");
|
|
|
|
const ManifestPlugin = require("webpack-manifest-plugin");
|
|
|
|
const StaticSiteGeneratorPlugin = require("static-site-generator-webpack-plugin");
|
|
|
|
const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin");
|
|
|
|
const paths = require("./paths");
|
|
|
|
const getClientEnvironment = require("./env");
|
|
|
|
|
|
|
|
// Source maps are resource heavy and can cause out of memory issue for large source files.
|
|
|
|
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== "false";
|
|
|
|
|
|
|
|
async function makeConfig(
|
|
|
|
mode /*: "production" | "development" */
|
|
|
|
) /*: Promise<mixed> */ {
|
|
|
|
return {
|
|
|
|
// Don't attempt to continue if there are any errors.
|
|
|
|
bail: true,
|
|
|
|
// We generate sourcemaps in production. This is slow but gives good results.
|
|
|
|
// You can exclude the *.map files from the build during deployment.
|
|
|
|
devtool: shouldUseSourceMap ? "source-map" : false,
|
|
|
|
// In production, we only want to load the polyfills and the app code.
|
|
|
|
entry: {
|
|
|
|
main: [require.resolve("./polyfills"), paths.appIndexJs2],
|
|
|
|
ssr: [
|
|
|
|
require.resolve("./polyfills"),
|
|
|
|
paths.appServerSideRenderingIndexJs2,
|
|
|
|
],
|
|
|
|
},
|
|
|
|
devServer: {
|
|
|
|
inline: false,
|
|
|
|
before: (app /*: ExpressApp */) => {
|
Configure fe2 to load from instances (#1856)
This modifies the frontend2 so that we can load real data from cli2
instances, including:
- the root `sourcecred.json` config
- files inside the `config/` directory
- files inside the `output/` directory
This works both for the dev server and the compiled output.
In the case of the dev server, it's now necessary to provide the path to
the cli2 instance you're developing against. You can pass this via the
`--instance` argument, as in `yarn start2 --instance=/path/`, or via
the `$SOURCECRED_DEV_INSTANCE` environment variable (recommended). If
neither is provided, or if that path doesn't look like a valid instance,
an error is thrown.
In the case of the built output, given a valid sc2 instance, you can set
it up via the following:
```
cp -r build2/favicon.png build2/index.html build2/static $INSTANCE
```
Then spin up a simple http server in the $INSTANCE.
You can look at console messages in the frontend to verify that the
instance is working (note it expects to see a discourse config file, not
a GitHub config file as was used in some previous examples).
Test plan:
Setup an example Discourse instance, and then turn on the server via
`yarn start2 --instance=PATH`,
`SOURCECRED_DEV_INSTANCE=path yarn start2`,
and by manually copying in the built outputs using the instructions
above.
In each case, you can load the homepage and check the console to see
that assets loaded successfully.
2020-06-18 01:47:12 +00:00
|
|
|
let developmentInstancePath /*: ?string */ =
|
|
|
|
process.env["SOURCECRED_DEV_INSTANCE"];
|
|
|
|
const lastArg = process.argv[process.argv.length - 1];
|
|
|
|
if (lastArg.startsWith("--instance")) {
|
|
|
|
const pieces = lastArg.split("=");
|
|
|
|
developmentInstancePath = pieces[1];
|
|
|
|
}
|
|
|
|
if (developmentInstancePath == null) {
|
|
|
|
throw new Error(
|
|
|
|
"Please provide a SourceCred cli2 instance, via $SOURCECRED_DEV_INSTANCE, or --instance=PATH"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
const configPath = path.join(
|
|
|
|
developmentInstancePath,
|
|
|
|
"sourcecred.json"
|
|
|
|
);
|
|
|
|
if (!fs.existsSync(configPath)) {
|
|
|
|
// Sanity check; we won't try to verify that the right output files are there, since it may change and the
|
|
|
|
// frontend should have helpful error messages in that case. But if there's no sourcecred.json file, then
|
|
|
|
// it's likely the developer has made a mistake and would find a quick failure helpful.
|
|
|
|
throw new Error(
|
|
|
|
`Provided instance directory ${developmentInstancePath} doesn't have a sourcecred.json file`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const configContents = fs.readFileSync(configPath);
|
|
|
|
const serveConfig = (_unused_req, res /*: ExpressResponse */) => {
|
|
|
|
res.status(200).send(configContents);
|
|
|
|
};
|
2020-06-18 01:44:34 +00:00
|
|
|
const rejectCache = (_unused_req, res /*: ExpressResponse */) => {
|
|
|
|
res.status(400).send("Bad Request: Cache unavailable at runtime\n");
|
|
|
|
};
|
Configure fe2 to load from instances (#1856)
This modifies the frontend2 so that we can load real data from cli2
instances, including:
- the root `sourcecred.json` config
- files inside the `config/` directory
- files inside the `output/` directory
This works both for the dev server and the compiled output.
In the case of the dev server, it's now necessary to provide the path to
the cli2 instance you're developing against. You can pass this via the
`--instance` argument, as in `yarn start2 --instance=/path/`, or via
the `$SOURCECRED_DEV_INSTANCE` environment variable (recommended). If
neither is provided, or if that path doesn't look like a valid instance,
an error is thrown.
In the case of the built output, given a valid sc2 instance, you can set
it up via the following:
```
cp -r build2/favicon.png build2/index.html build2/static $INSTANCE
```
Then spin up a simple http server in the $INSTANCE.
You can look at console messages in the frontend to verify that the
instance is working (note it expects to see a discourse config file, not
a GitHub config file as was used in some previous examples).
Test plan:
Setup an example Discourse instance, and then turn on the server via
`yarn start2 --instance=PATH`,
`SOURCECRED_DEV_INSTANCE=path yarn start2`,
and by manually copying in the built outputs using the instructions
above.
In each case, you can load the homepage and check the console to see
that assets loaded successfully.
2020-06-18 01:47:12 +00:00
|
|
|
|
|
|
|
app.get("/sourcecred.json", serveConfig);
|
|
|
|
app.get(`/cache`, rejectCache);
|
|
|
|
app.get(`/cache/*`, rejectCache);
|
|
|
|
|
|
|
|
// It's important that we individually whitelist directories (and the
|
|
|
|
// sourcecred.json file) rather than indiscriminately serving from
|
|
|
|
// root, because there might be a "permanent" frontend installed in
|
|
|
|
// that instance, and we don't want to accidentally load the existing
|
|
|
|
// frontend instead of our development copy.
|
|
|
|
app.use(
|
|
|
|
"/output/",
|
|
|
|
express.static(path.join(developmentInstancePath, "output"))
|
|
|
|
);
|
2020-06-18 01:44:34 +00:00
|
|
|
app.use(
|
Configure fe2 to load from instances (#1856)
This modifies the frontend2 so that we can load real data from cli2
instances, including:
- the root `sourcecred.json` config
- files inside the `config/` directory
- files inside the `output/` directory
This works both for the dev server and the compiled output.
In the case of the dev server, it's now necessary to provide the path to
the cli2 instance you're developing against. You can pass this via the
`--instance` argument, as in `yarn start2 --instance=/path/`, or via
the `$SOURCECRED_DEV_INSTANCE` environment variable (recommended). If
neither is provided, or if that path doesn't look like a valid instance,
an error is thrown.
In the case of the built output, given a valid sc2 instance, you can set
it up via the following:
```
cp -r build2/favicon.png build2/index.html build2/static $INSTANCE
```
Then spin up a simple http server in the $INSTANCE.
You can look at console messages in the frontend to verify that the
instance is working (note it expects to see a discourse config file, not
a GitHub config file as was used in some previous examples).
Test plan:
Setup an example Discourse instance, and then turn on the server via
`yarn start2 --instance=PATH`,
`SOURCECRED_DEV_INSTANCE=path yarn start2`,
and by manually copying in the built outputs using the instructions
above.
In each case, you can load the homepage and check the console to see
that assets loaded successfully.
2020-06-18 01:47:12 +00:00
|
|
|
"/config/",
|
|
|
|
express.static(path.join(developmentInstancePath, "config"))
|
2020-06-18 01:44:34 +00:00
|
|
|
);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
output: {
|
|
|
|
// The build folder.
|
|
|
|
path: paths.appBuild2,
|
|
|
|
// Generated JS file names (with nested folders).
|
|
|
|
// There will be one main bundle, and one file per asynchronous chunk.
|
|
|
|
// We don't currently advertise code splitting but Webpack supports it.
|
|
|
|
filename: "static/js/[name].[chunkhash:8].js",
|
|
|
|
chunkFilename: "static/js/[name].[chunkhash:8].chunk.js",
|
|
|
|
// Point sourcemap entries to original disk location (format as URL on Windows)
|
|
|
|
devtoolModuleFilenameTemplate: (
|
|
|
|
info /*:
|
|
|
|
{|
|
|
|
|
// https://webpack.js.org/configuration/output/#output-devtoolmodulefilenametemplate
|
|
|
|
+absoluteResourcePath: string,
|
|
|
|
+allLoaders: string,
|
|
|
|
+hash: string,
|
|
|
|
+id: string,
|
|
|
|
+loaders: string,
|
|
|
|
+resource: string,
|
|
|
|
+resourcePath: string,
|
|
|
|
+namespace: string,
|
|
|
|
|}
|
|
|
|
*/
|
|
|
|
) =>
|
|
|
|
path
|
|
|
|
.relative(paths.appSrc, info.absoluteResourcePath)
|
|
|
|
.replace(/\\/g, "/"),
|
|
|
|
// We need to use a UMD module to build the static site.
|
|
|
|
libraryTarget: "umd",
|
|
|
|
globalObject: "this",
|
|
|
|
},
|
|
|
|
resolve: {
|
|
|
|
// This allows you to set a fallback for where Webpack should look for modules.
|
|
|
|
// We placed these paths second because we want `node_modules` to "win"
|
|
|
|
// if there are any conflicts. This matches Node resolution mechanism.
|
|
|
|
// https://github.com/facebookincubator/create-react-app/issues/253
|
|
|
|
modules: [
|
|
|
|
"node_modules",
|
|
|
|
paths.appNodeModules,
|
|
|
|
...(process.env.NODE_PATH || "").split(path.delimiter).filter(Boolean),
|
|
|
|
],
|
|
|
|
// These are the reasonable defaults supported by the Node ecosystem.
|
|
|
|
// We also include JSX as a common component filename extension to support
|
|
|
|
// some tools, although we do not recommend using it, see:
|
|
|
|
// https://github.com/facebookincubator/create-react-app/issues/290
|
|
|
|
// `web` extension prefixes have been added for better support
|
|
|
|
// for React Native Web.
|
|
|
|
extensions: [".web.js", ".mjs", ".js", ".json", ".web.jsx", ".jsx"],
|
|
|
|
alias: {
|
|
|
|
// Support React Native Web
|
|
|
|
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
|
|
|
|
"react-native": "react-native-web",
|
|
|
|
},
|
|
|
|
plugins: [
|
|
|
|
// Prevents users from importing files from outside of src/ (or node_modules/).
|
|
|
|
// This often causes confusion because we only process files within src/ with babel.
|
|
|
|
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
|
|
|
|
// please link the files into your node_modules/ and let module-resolution kick in.
|
|
|
|
// Make sure your source files are compiled, as they will not be processed in any way.
|
|
|
|
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
|
|
|
|
],
|
|
|
|
},
|
|
|
|
module: {
|
|
|
|
strictExportPresence: true,
|
|
|
|
rules: [
|
|
|
|
// TODO: Disable require.ensure as it's not a standard language feature.
|
|
|
|
// We are waiting for https://github.com/facebookincubator/create-react-app/issues/2176.
|
|
|
|
// { parser: { requireEnsure: false } },
|
|
|
|
{
|
|
|
|
// "oneOf" will traverse all following loaders until one will
|
|
|
|
// match the requirements. When no loader matches it will fall
|
|
|
|
// back to the "file" loader at the end of the loader list.
|
|
|
|
oneOf: [
|
|
|
|
// "url" loader works just like "file" loader but it also embeds
|
|
|
|
// assets smaller than specified size as data URLs to avoid requests.
|
|
|
|
{
|
|
|
|
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
|
|
|
|
loader: require.resolve("url-loader"),
|
|
|
|
options: {
|
|
|
|
limit: 10000,
|
|
|
|
name: "static/media/[name].[hash:8].[ext]",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Process JS with Babel.
|
|
|
|
{
|
|
|
|
test: /\.(js|jsx|mjs)$/,
|
|
|
|
include: paths.appSrc,
|
|
|
|
loader: require.resolve("babel-loader"),
|
|
|
|
options: {
|
|
|
|
compact: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
test: /\.css$/,
|
|
|
|
loader: "css-loader", // TODO(@wchargin): add csso-loader
|
|
|
|
},
|
|
|
|
{
|
|
|
|
test: /\.svg$/,
|
|
|
|
exclude: /node_modules/,
|
|
|
|
loader: "svg-react-loader",
|
|
|
|
},
|
|
|
|
// "file" loader makes sure assets end up in the `build` folder.
|
|
|
|
// When you `import` an asset, you get its filename.
|
|
|
|
// This loader doesn't use a "test" so it will catch all modules
|
|
|
|
// that fall through the other loaders.
|
|
|
|
{
|
|
|
|
loader: require.resolve("file-loader"),
|
|
|
|
// Exclude `js` files to keep "css" loader working as it injects
|
|
|
|
// it's runtime that would otherwise processed through "file" loader.
|
|
|
|
// Also exclude `html` and `json` extensions so they get processed
|
|
|
|
// by webpacks internal loaders.
|
|
|
|
exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/],
|
|
|
|
options: {
|
|
|
|
name: "static/media/[name].[hash:8].[ext]",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// ** STOP ** Are you adding a new loader?
|
|
|
|
// Make sure to add the new loader(s) before the "file" loader.
|
|
|
|
],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
plugins: await plugins(mode),
|
|
|
|
// Some libraries import Node modules but don't use them in the browser.
|
|
|
|
// Tell Webpack to provide empty mocks for them so importing them works.
|
|
|
|
node: {
|
|
|
|
dgram: "empty",
|
|
|
|
fs: "empty",
|
|
|
|
net: "empty",
|
|
|
|
tls: "empty",
|
|
|
|
child_process: "empty",
|
|
|
|
},
|
|
|
|
mode,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
async function plugins(mode /*: "development" | "production" */) {
|
2020-06-18 01:45:50 +00:00
|
|
|
// TODO: When we have switched fully to the instance system, we can remove
|
|
|
|
// the projectIds argument.
|
|
|
|
const env = getClientEnvironment(null);
|
2020-06-18 01:44:34 +00:00
|
|
|
const basePlugins = [
|
|
|
|
new StaticSiteGeneratorPlugin({
|
|
|
|
entry: "ssr",
|
Remove frontend2 routing and radically simplify
Per discussion with @hammadj, @topocount, and @wchargin, we are planning
to have the frontend2 system use react-admin at the top level. Per
investigation by @topocount, react-admin conflicts with the older
version of react-router that we use.
As such, this commit wildly simplifies the homepage2 system so we no
longer have any routing, and instead we just statically render the
index.html file. We also removed the `Assets` type, not because we are
sure we don't need it, but because we didn't want to debug it while we
were all pairing. @wchargin offered to fix it up later.
Test plan:
- run `yarn start2 --instance=PATH` and observe that the "Under
Construction" message displays, along with console messages showing that
data loaded successfully.
- run `yarn build2` and copy files from `build2` into the root of a cli2
instance. Run an http server in that instance, and observe that the
frontend displays properly per instructions above.
Paired with: @wchargin
Paired with: @hammadj
Paired with: @topocount
2020-06-18 02:43:34 +00:00
|
|
|
paths: ["/"],
|
2020-06-18 01:44:34 +00:00
|
|
|
locals: {},
|
|
|
|
}),
|
|
|
|
new CopyPlugin([{from: paths.favicon, to: "favicon.png"}]),
|
|
|
|
// Makes some environment variables available to the JS code, for example:
|
|
|
|
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
|
|
|
|
// It is absolutely essential that NODE_ENV was set to production here.
|
|
|
|
// Otherwise React will be compiled in the very slow development mode.
|
|
|
|
new webpack.DefinePlugin(env.stringified),
|
|
|
|
// Generate a manifest file which contains a mapping of all asset filenames
|
|
|
|
// to their corresponding output file so that tools can pick it up without
|
|
|
|
// having to parse `index.html`.
|
|
|
|
new ManifestPlugin({
|
|
|
|
fileName: "asset-manifest.json",
|
|
|
|
}),
|
|
|
|
// Moment.js is an extremely popular library that bundles large locale files
|
|
|
|
// by default due to how Webpack interprets its code. This is a practical
|
|
|
|
// solution that requires the user to opt into importing specific locales.
|
|
|
|
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
|
|
|
|
// You can remove this if you don't use Moment.js:
|
|
|
|
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
|
|
|
];
|
|
|
|
const prodOnlyPlugins = [
|
|
|
|
// Remove the output directory before starting the build.
|
|
|
|
new RemoveBuildDirectoryPlugin(),
|
|
|
|
];
|
|
|
|
switch (mode) {
|
|
|
|
case "development":
|
|
|
|
return basePlugins;
|
|
|
|
case "production":
|
|
|
|
return basePlugins.concat(prodOnlyPlugins);
|
|
|
|
default:
|
|
|
|
throw new Error(/*:: (*/ mode /*: empty) */);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getMode() {
|
|
|
|
const mode = process.env.NODE_ENV;
|
|
|
|
if (mode !== "production" && mode !== "development") {
|
|
|
|
throw new Error("unknown mode: " + String(mode));
|
|
|
|
}
|
|
|
|
return mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = makeConfig(getMode());
|