Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Procfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
web: WEBPACK_PORT=${WEBPACK_PORT:-3035} bundle exec rackup config.ru --port ${PORT:-3000} --host ${FOREMAN_HOST:-${HOST:-localhost}}
worker: bundle exec good_job start
js: WEBPACK_PORT=${WEBPACK_PORT:-3035} yarn webpack $([ -n "$HTTPS" ] && echo "--watch" || echo "serve")
js: WEBPACK_PORT=${WEBPACK_PORT:-3035} yarn webpack --watch
css: yarn build:css --watch
46 changes: 46 additions & 0 deletions app/javascript/packages/lite-webpack-dev-server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# `@18f/identity-lite-webpack-dev-server`

Minimal, zero-dependency alternative to Webpack's default development web server.
Comment thread
zachmargolis marked this conversation as resolved.
Outdated

**What does it do the same as `webpack-dev-server`?**

- Serves static assets from the built output directory
- Pauses page loads during compilation to guarantee that a page loads with the latest JavaScript

**What doesn't it do that `webpack-dev-server` does?**

Most everything else! Notably, it does not:

- Automatically reload the page when compilation finishes
- Handle anything other than JavaScript

## Usage

If migrating from `webpack-dev-server`:

- Remove your `devServer` configuration from `webpack.config.js`

Add an instance of `LiteWebpackDevServerPlugin` to your Webpack `plugins` array. The example below
shows how you might conditionally include the plugin in local development only, to avoid the server
being run in production environments.

```ts
// webpack.config.js

const { DEV_SERVER_PORT } = process.env;

export default {
// ...
plugins: [
// ...
DEV_SERVER_PORT &&
new LiteWebpackDevServerPlugin({ publicPath: './public', port: Number(DEV_SERVER_PORT) }),
]
};
```

Supported options:

- `publicPath` (`string`): Relative path to the root of the static file server
- `port` (`number`): Port on which the static file server should listen
- `headers` (`object`): Additional headers to include in every response
7 changes: 7 additions & 0 deletions app/javascript/packages/lite-webpack-dev-server/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "@18f/identity-lite-webpack-dev-server",
"version": "1.0.0",
"private": true,
"sideEffects": false,
"main": "./webpack-plugin.js"
}
85 changes: 85 additions & 0 deletions app/javascript/packages/lite-webpack-dev-server/webpack-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
const http = require('node:http');
const { join } = require('node:path');
const { createReadStream } = require('node:fs');

/**
* @typedef PluginOptions
* @prop {string} [publicPath]
* @prop {number} [port]
* @prop {Record<string, string>} [headers]
*/

/**
* Webpack plugin name.
*
* @type {string}
*/
const PLUGIN = 'LiteWebpackDevServerPlugin';

class LiteWebpackDevServerPlugin {
/**
* @type {string}
*/
publicPath;

/**
* @type {number}
*/
port;

/**
* @type {Record<string, string>}
*/
headers;

/**
* @param {PluginOptions} options
*/
constructor(options) {
Object.assign(this, {
publicPath: '.',
port: 3035,
headers: {
'content-type': 'text/javascript',
...options.headers,
},
...options,
});
}

/**
* @param {import('webpack').Compiler} compiler
*/
apply(compiler) {
/** @type {Promise<void>} */
let build;

/** @type {() => void} */
let onCompileFinished;

const server = http.createServer(async (request, response) => {
for (const [key, value] of Object.entries(this.headers)) {
response.setHeader(key, value);
}

await build;
const url = new URL(request.url ?? '', 'file:///');
const filePath = join(process.cwd(), this.publicPath, url.pathname);
createReadStream(filePath).pipe(response);
});

server.listen(this.port);

compiler.hooks.beforeCompile.tap(PLUGIN, () => {
build = new Promise((resolve) => {
onCompileFinished = resolve;
});
});

compiler.hooks.afterCompile.tap(PLUGIN, () => onCompileFinished());

compiler.hooks.shutdown.tap(PLUGIN, () => server.close());
}
}

module.exports = LiteWebpackDevServerPlugin;
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@
"svgo": "^3.2.0",
"swr": "^2.0.0",
"typescript": "^5.2.2",
"webpack-dev-server": "^5.0.4",
"yarn-deduplicate": "^6.0.2"
},
"resolutions": {
Expand Down
29 changes: 11 additions & 18 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const WebpackAssetsManifest = require('webpack-assets-manifest');
const RailsI18nWebpackPlugin = require('@18f/identity-rails-i18n-webpack-plugin');
const RailsAssetsWebpackPlugin = require('@18f/identity-assets/webpack-plugin');
const UnpolyfillWebpackPlugin = require('@18f/identity-unpolyfill-webpack-plugin');
const LiteWebpackDevServerPlugin = require('@18f/identity-lite-webpack-dev-server');

const env = process.env.NODE_ENV || process.env.RAILS_ENV || 'development';
const host = process.env.HOST || 'localhost';
Expand All @@ -22,24 +23,6 @@ module.exports = /** @type {import('webpack').Configuration} */ ({
mode,
devtool,
target: ['web'],
devServer: {
static: {
directory: './public',
watch: false,
},
port: devServerPort,
headers: {
'Access-Control-Allow-Origin': '*',
'Cache-Control': 'no-store',
Vary: '*',
},
client: {
overlay: {
runtimeErrors: false,
},
},
hot: false,
},
entry: entries.reduce((result, path) => {
result[parse(path).name] = resolve(path);
return result;
Expand Down Expand Up @@ -112,5 +95,15 @@ module.exports = /** @type {import('webpack').Configuration} */ ({
}),
new RailsAssetsWebpackPlugin(),
new UnpolyfillWebpackPlugin(),
devServerPort &&
new LiteWebpackDevServerPlugin({
publicPath: './public',
port: Number(devServerPort),
headers: {
'Access-Control-Allow-Origin': '*',
'Cache-Control': 'no-store',
Vary: '*',
},
}),
],
});
Loading