anypack-plugin-serve 0.1.0-beta.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -25,6 +25,9 @@ const { validate } = require('./validate');
25
25
  const defaults = {
26
26
  // leave `client` undefined
27
27
  // client: null,
28
+ client: {
29
+ retry: true,
30
+ },
28
31
  compress: null,
29
32
  headers: null,
30
33
  historyFallback: false,
@@ -46,7 +49,8 @@ const defaults = {
46
49
  const key = 'anypack-plugin-serve';
47
50
  const nanoid = customAlphabet('1234567890abcdef', 7);
48
51
 
49
- let instance = null;
52
+ let activeInstanceId = null;
53
+ let activeLog = null;
50
54
 
51
55
  class AnypackPluginServe extends EventEmitter {
52
56
  constructor(opts = {}) {
@@ -65,16 +69,18 @@ class AnypackPluginServe extends EventEmitter {
65
69
  // of the plugin to be tested within the same context. If you find this, use this at your own
66
70
  // peril.
67
71
  /* istanbul ignore if */
68
- if (!opts.allowMany && instance) {
69
- instance.log.error(
72
+ if (!opts.allowMany && activeInstanceId !== null) {
73
+ activeLog.error(
70
74
  'Duplicate instances created. Only the first instance of this plugin will be active.',
71
75
  );
72
76
  return;
73
77
  }
74
78
 
75
- instance = this;
79
+ const instanceId = Symbol();
80
+ this._instanceId = instanceId;
81
+ activeInstanceId = instanceId;
76
82
 
77
- const options = Object.assign({}, defaults, opts);
83
+ const options = { ...defaults, ...opts };
78
84
 
79
85
  if (options.compress === true) {
80
86
  options.compress = {};
@@ -107,6 +113,7 @@ class AnypackPluginServe extends EventEmitter {
107
113
  }
108
114
 
109
115
  this.log = getLogger(options.log || {});
116
+ activeLog = this.log;
110
117
  this.options = options;
111
118
  this.compilers = [];
112
119
  this.state = {};
@@ -117,7 +124,7 @@ class AnypackPluginServe extends EventEmitter {
117
124
 
118
125
  // only allow once instance of the plugin to run for a build
119
126
  /* istanbul ignore if */
120
- if (instance !== this) {
127
+ if (activeInstanceId !== this._instanceId) {
121
128
  return;
122
129
  }
123
130
 
@@ -125,13 +132,9 @@ class AnypackPluginServe extends EventEmitter {
125
132
  }
126
133
 
127
134
  attach() {
128
- const self = this;
129
- const result = {
130
- apply(compiler) {
131
- return self.hook(compiler);
132
- },
135
+ return {
136
+ apply: (compiler) => this.hook(compiler),
133
137
  };
134
- return result;
135
138
  }
136
139
 
137
140
  // #138. handle emitted events that don't have a listener registered so they can be sent via WebSocket
@@ -151,7 +154,8 @@ class AnypackPluginServe extends EventEmitter {
151
154
  }
152
155
 
153
156
  hook(compiler) {
154
- const { done, invalid, watchClose, watchRun } = compiler.hooks;
157
+ const { done, invalid, watchClose, watchRun, beforeCompile } =
158
+ compiler.hooks;
155
159
 
156
160
  if (!compiler.wpsId) {
157
161
  compiler.wpsId = nanoid();
@@ -199,6 +203,7 @@ class AnypackPluginServe extends EventEmitter {
199
203
 
200
204
  // we do this emit because webpack caches and optimizes the hooks, so there's no way to detach
201
205
  // a listener/hook.
206
+ beforeCompile.tap(key, () => this.emit('build', compiler.name, compiler));
202
207
  done.tap(key, (stats) => this.emit('done', stats, compiler));
203
208
  invalid.tap(key, (filePath) => this.emit('invalid', filePath, compiler));
204
209
  watchClose.tap(key, () => this.emit('close', compiler));
@@ -234,7 +239,7 @@ class AnypackPluginServe extends EventEmitter {
234
239
  wpsId: compiler.wpsId,
235
240
  };
236
241
 
237
- const defineObject = Object.assign({}, this.options, compilerData);
242
+ const defineObject = { ...this.options, ...compilerData };
238
243
  const defineData = { ʎɐɹɔosǝʌɹǝs: JSON.stringify(defineObject) };
239
244
  const definePlugin = new compiler.webpack.DefinePlugin(defineData);
240
245
 
package/lib/middleware.js CHANGED
@@ -126,11 +126,11 @@ const getBuiltins = (app, options) => {
126
126
  }
127
127
  };
128
128
 
129
- const statik = (root, opts = { dev: true }) => {
130
- const paths = [].concat(root || options.static);
129
+ const statik = (root, opts) => {
130
+ const paths = [root || options.static].flat();
131
131
  staticPaths = paths;
132
132
  for (const path of paths) {
133
- app.use(sirv(path, opts));
133
+ app.use(sirv(path, opts ?? { dev: true }));
134
134
  }
135
135
  };
136
136
 
@@ -27,15 +27,15 @@ const init = function init(compiler, log) {
27
27
  );
28
28
 
29
29
  /* istanbul ignore else */
30
- if (!hasHMRPlugin) {
31
- addPlugin(compiler);
32
- } else {
30
+ if (hasHMRPlugin) {
33
31
  log.error(
34
32
  'anypack-plugin-serve adds HotModuleReplacementPlugin automatically. Please remove it from your config.',
35
33
  );
36
34
  throw new PluginExistsError(
37
35
  'HotModuleReplacementPlugin exists in the specified configuration.',
38
36
  );
37
+ } else {
38
+ addPlugin(compiler);
39
39
  }
40
40
  };
41
41
 
@@ -29,8 +29,9 @@ const readPkgName = () => {
29
29
  try {
30
30
  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
31
31
  return pkg.name;
32
- } catch (_error) {
32
+ } catch (error) {
33
33
  // istanbul ignore next
34
+ console.warn(`Could not read package name: ${error.message}`);
34
35
  return null;
35
36
  }
36
37
  };
@@ -102,18 +102,17 @@ function run(command, ...args) {
102
102
 
103
103
  class Ramdisk {
104
104
  constructor(opts = {}) {
105
- const partialOpts = Object.assign({}, defaults, opts);
106
- const options = Object.assign({}, partialOpts, {
105
+ const partialOpts = { ...defaults, ...opts };
106
+ const options = {
107
+ ...partialOpts,
107
108
  blocks: partialOpts.bytes / partialOpts.blockSize,
108
- });
109
+ };
109
110
 
110
111
  this.options = options;
111
112
 
112
113
  this.commands = getCommands();
113
114
  this.options.diskPath = this.commands?.root(this.options);
114
115
 
115
- // const options = Object.assign({}, opts, { root: commands.root });
116
-
117
116
  this.init();
118
117
  }
119
118
 
package/lib/routes.js CHANGED
@@ -11,60 +11,65 @@
11
11
  const { unstyle } = require('ansi-colors');
12
12
  const stringify = require('json-stringify-safe');
13
13
 
14
- const prep = (data) => stringify(data);
15
-
16
- const statsOptions = {
17
- all: false,
18
- cached: true,
19
- children: true,
20
- hash: true,
21
- modules: true,
22
- timings: true,
23
- exclude: ['node_modules', 'bower_components', 'components'],
24
- };
14
+ function registerEvent(ee, socket, event, handler) {
15
+ ee.on(event, handler);
16
+
17
+ socket.on('close', () => {
18
+ ee.removeListener(event, handler);
19
+ });
20
+ }
25
21
 
26
22
  const setupRoutes = function setupRoutes() {
27
23
  const { app, options } = this;
28
- const events = ['build', 'done', 'invalid', 'progress'];
24
+
25
+ let buildDone = false;
26
+ this.on('build', () => {
27
+ buildDone = false;
28
+ });
29
+ this.on('done', () => {
30
+ buildDone = true;
31
+ });
32
+
29
33
  const connect = async (req, _res) => {
30
34
  if (req.ws) {
31
35
  const socket = await req.ws();
32
- const send = (data) => {
36
+ let lastHash = null;
37
+ const send = (action, data) => {
33
38
  if (socket.readyState !== 1) {
34
39
  return;
35
40
  }
36
- socket.send(data);
41
+ socket.send(stringify({ action, data }));
37
42
  };
38
43
 
39
- socket.build = (compilerName = '<unknown>', { wpsId }) => {
40
- send(prep({ action: 'build', data: { compilerName, wpsId } }));
41
- };
44
+ registerEvent(this, socket, 'build', (compilerName, { wpsId }) => {
45
+ send('build', { compilerName: compilerName ?? '<unknown>', wpsId });
46
+ });
42
47
 
43
- socket.done = (stats, { wpsId }) => {
48
+ registerEvent(this, socket, 'done', (stats, { wpsId }) => {
44
49
  const { hash } = stats;
45
50
 
46
- if (socket.lastHash === hash) {
51
+ if (lastHash === hash) {
47
52
  return;
48
53
  }
49
54
 
50
- send(prep({ action: 'done', data: { hash, wpsId } }));
55
+ send('done', { hash, wpsId });
51
56
 
52
- socket.lastHash = hash;
57
+ lastHash = hash;
53
58
 
54
- const { errors = [], warnings = [] } = stats.toJson(statsOptions);
59
+ if (stats.hasErrors() || stats.hasWarnings()) {
60
+ const renderedStats = stats.toJson('errors-warnings');
61
+ const { errors = [], warnings = [] } = renderedStats;
55
62
 
56
- if (errors.length || warnings.length) {
57
- send(
58
- prep({
59
- action: 'problems',
60
- data: {
61
- errors: errors.slice(0).map((e) => unstyle(e)),
62
- hash,
63
- warnings: warnings.slice(0).map((e) => unstyle(e)),
64
- wpsId,
65
- },
66
- }),
67
- );
63
+ send('problems', {
64
+ hash,
65
+ wpsId,
66
+ errors: errors
67
+ .slice(0)
68
+ .map((e) => ({ ...e, message: unstyle(e.message) })),
69
+ warnings: warnings
70
+ .slice(0)
71
+ .map((e) => ({ ...e, message: unstyle(e.message) })),
72
+ });
68
73
 
69
74
  if (errors.length) {
70
75
  return;
@@ -73,41 +78,31 @@ const setupRoutes = function setupRoutes() {
73
78
 
74
79
  if (options.hmr || options.liveReload) {
75
80
  const action = options.liveReload ? 'reload' : 'replace';
76
- send(prep({ action, data: { hash, wpsId } }));
81
+ send(action, { hash, wpsId });
77
82
  }
78
- };
83
+ });
79
84
 
80
- socket.invalid = (filePath = '<unknown>', compiler) => {
85
+ registerEvent(this, socket, 'invalid', (filePath, compiler) => {
86
+ const path = filePath ?? '<unknown>';
81
87
  const context =
82
88
  compiler.context || compiler.options.context || process.cwd();
83
- const fileName = filePath?.replace?.(context, '') || filePath;
89
+ const fileName = path?.replace?.(context, '') || path;
84
90
  const { wpsId } = compiler;
85
91
 
86
- send(prep({ action: 'invalid', data: { fileName, wpsId } }));
87
- };
88
-
89
- socket.progress = (data) => {
90
- send(prep({ action: 'progress', data }));
91
- };
92
-
93
- for (const event of events) {
94
- this.on(event, socket[event]);
92
+ send('invalid', { fileName, wpsId });
93
+ });
95
94
 
96
- socket.on('close', () => {
97
- this.removeListener(event, socket[event]);
98
- });
99
- }
95
+ registerEvent(this, socket, 'progress', (data) => {
96
+ send('progress', data);
97
+ });
100
98
 
101
99
  // #138. handle emitted events that don't have a listener registered, and forward the message
102
100
  // onto the client via the socket
103
- const unhandled = ({ eventName, data }) =>
104
- send(prep({ action: eventName, data }));
105
- this.on('unhandled', unhandled);
106
- socket.on('close', () => {
107
- this.off('unhandled', unhandled);
101
+ registerEvent(this, socket, 'unhandled', ({ eventName, data }) => {
102
+ send(eventName, data);
108
103
  });
109
104
 
110
- send(prep({ action: 'connected' }));
105
+ send('connected', { buildDone });
111
106
  }
112
107
  };
113
108
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anypack-plugin-serve",
3
- "version": "0.1.0-beta.1",
3
+ "version": "0.1.0",
4
4
  "description": "A Development Server in a Webpack Plugin",
5
5
  "license": "MPL-2.0",
6
6
  "repository": "onigoetz/anypack-plugin-serve",
@@ -33,6 +33,7 @@
33
33
  "dependencies": {
34
34
  "@polka/compression": "^1.0.0-next.25",
35
35
  "ansi-colors": "^4.1.3",
36
+ "anypack-overlay": "0.1.0",
36
37
  "connect-history-api-fallback": "^2.0.0",
37
38
  "empathic": "^2.0.0",
38
39
  "http-proxy-middleware": "^3.0.5",
@@ -47,14 +48,15 @@
47
48
  "ws": "^8.18.3"
48
49
  },
49
50
  "devDependencies": {
50
- "@biomejs/biome": "2.3.11",
51
- "@rstest/core": "^0.7.7",
52
- "@rstest/coverage-istanbul": "^0.1.4",
51
+ "@biomejs/biome": "2.4.10",
52
+ "@rstest/core": "^0.9.0",
53
+ "@rstest/coverage-istanbul": "^0.3.0",
53
54
  "del-cli": "7.0.0",
54
55
  "get-port": "^7.1.0",
55
56
  "node-fetch": "^3.3.2",
56
57
  "puppeteer": "^24.34.0",
57
58
  "random-js": "^2.1.0",
59
+ "rstest-sonar-reporter": "1.0.0",
58
60
  "webpack": "^5.0.0",
59
61
  "webpack-nano": "^1.0.0"
60
62
  },
@@ -66,4 +68,4 @@
66
68
  "server",
67
69
  "webpack"
68
70
  ]
69
- }
71
+ }
@@ -1,114 +0,0 @@
1
- /*
2
- Copyright © 2018 Andrew Powell
3
-
4
- This Source Code Form is subject to the terms of the Mozilla Public
5
- License, v. 2.0. If a copy of the MPL was not distributed with this
6
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
-
8
- The above copyright notice and this permission notice shall be
9
- included in all copies or substantial portions of this Source Code Form.
10
- */
11
- const run = (buildHash, options) => {
12
- const { address, client = {}, hmr, progress, secure, status } = options;
13
-
14
- options.firstInstance = !window.anypackPluginServe;
15
-
16
- window.anypackPluginServe = window.anypackPluginServe || {
17
- compilers: {},
18
- };
19
- window.anypackPluginServe.silent = !!client.silent;
20
-
21
- const { ClientSocket } = require('./ClientSocket.js');
22
- const { replace } = require('./hmr.js');
23
- const { error, info, warn } = require('./log.js')();
24
-
25
- const protocol = secure ? 'wss' : 'ws';
26
- const socket = new ClientSocket(
27
- client,
28
- `${client.protocol || protocol}://${client.address || address}/wps`,
29
- );
30
-
31
- const { compilerName } = options;
32
-
33
- window.anypackPluginServe.compilers[compilerName] = {};
34
-
35
- // prevents ECONNRESET errors on the server
36
- window.addEventListener('beforeunload', () => socket.close());
37
-
38
- socket.addEventListener('message', (message) => {
39
- const { action, data = {} } = JSON.parse(message.data);
40
- const { errors, hash = '<?>', warnings } = data || {};
41
- const shortHash = hash.slice(0, 7);
42
- const identifier = options.compilerName
43
- ? `(Compiler: ${options.compilerName}) `
44
- : '';
45
- const compiler = window.anypackPluginServe.compilers[compilerName];
46
- const { wpsId } = data;
47
-
48
- switch (action) {
49
- case 'build':
50
- compiler.done = false;
51
- break;
52
- case 'connected':
53
- info(`WebSocket connected ${identifier}`);
54
- break;
55
- case 'done':
56
- compiler.done = true;
57
- break;
58
- case 'problems':
59
- if (data.errors.length) {
60
- error(`${identifier}Build ${shortHash} produced errors:\n`, errors);
61
- }
62
- if (data.warnings.length) {
63
- warn(
64
- `${identifier}Build ${shortHash} produced warnings:\n`,
65
- warnings,
66
- );
67
- }
68
- break;
69
- case 'reload':
70
- window.location.reload();
71
- break;
72
- case 'replace':
73
- // actions with a wpsId in tow indicate actions that should only be executed when the wpsId sent
74
- // matches the wpsId set in options. this is how we can identify multiple compilers in the
75
- // client.
76
- if (wpsId && wpsId === options.wpsId) {
77
- replace(buildHash, hash, hmr === 'refresh-on-failure');
78
- }
79
- break;
80
- default:
81
- }
82
- });
83
-
84
- if (options.firstInstance) {
85
- if (progress === 'minimal') {
86
- const { init } = require('./overlays/progress-minimal.js');
87
- init(options, socket);
88
- } else if (progress) {
89
- const { init } = require('./overlays/progress.js');
90
- init(options, socket);
91
- }
92
-
93
- if (status) {
94
- const { init } = require('./overlays/status.js');
95
- init(options, socket);
96
- }
97
-
98
- if (module.hot) {
99
- info('Hot Module Replacement is active');
100
-
101
- if (options.liveReload) {
102
- info('Live Reload taking precedence over Hot Module Replacement');
103
- }
104
- } else {
105
- warn('Hot Module Replacement is inactive');
106
- }
107
-
108
- if (!module.hot && options.liveReload) {
109
- info('Live Reload is active');
110
- }
111
- }
112
- };
113
-
114
- module.exports = { run };
@@ -1,118 +0,0 @@
1
- /*
2
- Copyright © 2018 Andrew Powell, Matheus Gonçalves da Silva
3
-
4
- This Source Code Form is subject to the terms of the Mozilla Public
5
- License, v. 2.0. If a copy of the MPL was not distributed with this
6
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
-
8
- The above copyright notice and this permission notice shall be
9
- included in all copies or substantial portions of this Source Code Form.
10
- */
11
- const { addCss, addHtml } = require('./util.js');
12
-
13
- const ns = 'wps-progress-minimal';
14
- const html = `
15
- <div id="${ns}" class="${ns}-hidden">
16
- <div id="${ns}-bar"></div>
17
- </div>
18
- `;
19
- const css = `
20
- #${ns} {
21
- position: fixed;
22
- top: 0;
23
- left: 0;
24
- height: 4px;
25
- width: 100vw;
26
- z-index: 2147483645;
27
- }
28
-
29
- #${ns}-bar {
30
- width: 0%;
31
- height: 4px;
32
- background-color: rgb(186, 223, 172);
33
- }
34
-
35
- @keyframes ${ns}-fade {
36
- 0% {
37
- opacity: 1;
38
- }
39
- 100% {
40
- opacity: 0;
41
- }
42
- }
43
-
44
- .${ns}-disappear {
45
- animation: ${ns}-fade .3s;
46
- animation-fill-mode: forwards;
47
- animation-delay: .5s;
48
- }
49
-
50
- .${ns}-hidden {
51
- display: none;
52
- }
53
- `;
54
-
55
- let hideOnPageVisible = false;
56
-
57
- const update = (percent) => {
58
- const bar = document.querySelector(`#${ns}-bar`);
59
- bar.style.width = `${percent}%`;
60
- };
61
-
62
- const reset = (wrapper) => {
63
- wrapper.classList.add(`${ns}-disappear`);
64
- };
65
-
66
- const init = (options, socket) => {
67
- if (options.firstInstance) {
68
- document.addEventListener('DOMContentLoaded', () => {
69
- addCss(css);
70
- addHtml(html);
71
-
72
- const wrapper = document.querySelector(`#${ns}`);
73
- wrapper.addEventListener('animationend', () => {
74
- update(0);
75
- wrapper.classList.add(`${ns}-hidden`);
76
- });
77
- });
78
-
79
- document.addEventListener('visibilitychange', () => {
80
- if (!document.hidden && hideOnPageVisible) {
81
- const wrapper = document.querySelector(`#${ns}`);
82
- reset(wrapper);
83
- hideOnPageVisible = false;
84
- }
85
- });
86
- }
87
-
88
- socket.addEventListener('message', (message) => {
89
- const { action, data } = JSON.parse(message.data);
90
-
91
- if (action !== 'progress') {
92
- return;
93
- }
94
-
95
- const percent = Math.floor(data.percent * 100);
96
- const wrapper = document.querySelector(`#${ns}`);
97
-
98
- if (wrapper) {
99
- wrapper.classList.remove(`${ns}-hidden`, `${ns}-disappear`);
100
- }
101
-
102
- if (data.percent === 1) {
103
- if (document.hidden) {
104
- hideOnPageVisible = true;
105
- } else {
106
- reset(wrapper);
107
- }
108
- } else {
109
- hideOnPageVisible = false;
110
- }
111
-
112
- update(percent);
113
- });
114
- };
115
-
116
- module.exports = {
117
- init,
118
- };