gristlabs_grist-core/buildtools/webpack.config.js
Paul Fitzpatrick e6983e9209 (core) add machinery for self-managed flavor of Grist
Summary:
Currently, we have two ways that we deliver Grist. One is grist-core,
which has simple defaults and is relatively easy for third parties to
deploy. The second is our internal build for our SaaS, which is the
opposite. For self-managed Grist, a planned paid on-premise version
of Grist, I adopt the following approach:

 * Use the `grist-core` build mechanism, extending it to accept an
   overlay of extra code if present.
 * Extra code is supplied in a self-contained `ext` directory, with
   an `ext/app` directory that is of same structure as core `app`
   and `stubs/app`.
 * The `ext` directory also contains information about extra
   node dependencies needed beyond that of `grist-core`.
 * The `ext` directory is contained within our monorepo rather than
   `grist-core` since it may contain material not under the Apache
   license.

Docker builds are achieved in our monorepo by using the `--build-context`
functionality to add in `ext` during the regular `grist-core` build:

```
docker buildx build --load -t gristlabs/grist-ee --build-context=ext=../ext .
```

Incremental builds in our monorepo are achieved with the `build_core.sh` helper,
like:

```
buildtools/build_core.sh /tmp/self-managed
cd /tmp/self-managed
yarn start
```

The initial `ext` directory contains material for snapshotting to S3.
If you build the docker image as above, and have S3 access, you can
do something like:

```
docker run -p 8484:8484 --env GRIST_SESSION_SECRET=a-secret \
  --env GRIST_DOCS_S3_BUCKET=grist-docs-test \
  --env GRIST_DOCS_S3_PREFIX=self-managed \
  -v $HOME/.aws:/root/.aws -it gristlabs/grist-ee
```

This will start a version of Grist that is like `grist-core` but with
S3 snapshots enabled. To release this code to `grist-core`, it would
just need to move from `ext/app` to `app` within core.

I tried a lot of ways of organizing self-managed Grist, and this was
what made me happiest. There are a lot of trade-offs, but here is what
I was looking for:

 * Only OSS-code in grist-core. Adding mixed-license material there
   feels unfair to people already working with the repo. That said,
   a possible future is to move away from our private monorepo to
   a public mixed-licence repo, which could have the same relationship
   with grist-core as the monorepo has.
 * Minimal differences between self-managed builds and one of our
   existing builds, ideally hewing as close to grist-core as possible
   for ease of documentation, debugging, and maintenance.
 * Ideally, docker builds without copying files around (the new
   `--build-context` functionality made that possible).
 * Compatibility with monorepo build.

Expressing dependencies of the extra code in `ext` proved tricky to
do in a clean way. Yarn/npm fought me every step of the way - everything
related to optional dependencies was unsatisfactory in some respect.
Yarn2 is flexible but smells like it might be overreach. In the end,
organizing to install non-core dependencies one directory up from the
main build was a good simple trick that saved my bacon.

This diff gets us to the point of building `grist-ee` images conveniently,
but there isn't a public repo people can go look at to see its source. This
could be generated by taking `grist-core`, adding the `ext` directory
to it, and pushing to a distinct repository. I'm not in a hurry to do that,
since a PR to that repo would be hard to sync with our monorepo and
`grist-core`. Also, we don't have any licensing text ready for the `ext`
directory. So leaving that for future work.

Test Plan: manual

Reviewers: georgegevoian, alexmojaki

Reviewed By: georgegevoian, alexmojaki

Differential Revision: https://phab.getgrist.com/D3415
2022-05-12 12:39:52 -04:00

59 lines
2.1 KiB
JavaScript

const StatsPlugin = require('stats-webpack-plugin');
const MomentLocalesPlugin = require('moment-locales-webpack-plugin');
const path = require('path');
module.exports = {
target: 'web',
entry: {
main: "app/client/app.js",
errorPages: "app/client/errorMain.js",
account: "app/client/accountMain.js",
},
output: {
filename: "[name].bundle.js",
sourceMapFilename: "[file].map",
path: path.resolve("./static"),
// Workaround for a known issue with webpack + onerror under chrome, see:
// https://github.com/webpack/webpack/issues/5681
// "We use a source map plugin here with this special configuration
// because if we do not - the window.onerror function does not work properly in chrome
// and it swallows the errors because normally source maps have begin with webpack:///
// here we are changing how the module file names are created
// See this bug
// https://bugs.chromium.org/p/chromium/issues/detail?id=765909
// See this for syntax
// https://webpack.js.org/configuration/output/#output-devtoolmodulefilenametemplate
// "
devtoolModuleFilenameTemplate: "[resourcePath]?[loaders]",
crossOriginLoading: "anonymous",
},
// This creates .map files, and takes webpack a couple of seconds to rebuild while developing,
// but provides correct mapping back to typescript, and allows breakpoints to be set in
// typescript ("cheap-module-eval-source-map" is faster, but breakpoints are largely broken).
devtool: "source-map",
resolve: {
modules: [
path.resolve('./_build'),
path.resolve('./_build/ext'),
path.resolve('./_build/stubs'),
path.resolve('./node_modules')
],
},
module: {
rules: [
{ test: /\.js$/,
use: ["source-map-loader"],
enforce: "pre"
}
]
},
plugins: [
new StatsPlugin(
'../.build_stats_js_bundle', // relative to output folder
{source: false}, // Omit sources, which unnecessarily make the stats file huge.
),
// To strip all locales except “en”
new MomentLocalesPlugin()
],
};