webpack-dev-service 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.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +17 -0
  3. package/client/cjs/client.cjs +128 -0
  4. package/client/cjs/events.cjs +64 -0
  5. package/client/cjs/hot.cjs +111 -0
  6. package/client/cjs/index.cjs +15 -0
  7. package/client/cjs/main.cjs +53 -0
  8. package/client/cjs/ui/overlay.cjs +236 -0
  9. package/client/cjs/ui/progress.cjs +94 -0
  10. package/client/cjs/ui/utils/ansi/index.cjs +443 -0
  11. package/client/cjs/ui/utils/ansi/regx.cjs +70 -0
  12. package/client/cjs/ui/utils/ansi/utils.cjs +27 -0
  13. package/client/cjs/ui/utils/index.cjs +126 -0
  14. package/client/esm/client.js +126 -0
  15. package/client/esm/events.js +60 -0
  16. package/client/esm/hot.js +106 -0
  17. package/client/esm/index.js +10 -0
  18. package/client/esm/main.js +51 -0
  19. package/client/esm/ui/overlay.js +234 -0
  20. package/client/esm/ui/progress.js +92 -0
  21. package/client/esm/ui/utils/ansi/index.js +441 -0
  22. package/client/esm/ui/utils/ansi/regx.js +66 -0
  23. package/client/esm/ui/utils/ansi/utils.js +24 -0
  24. package/client/esm/ui/utils/index.js +120 -0
  25. package/global.d.ts +14 -0
  26. package/package.json +121 -0
  27. package/server/cjs/dev.cjs +50 -0
  28. package/server/cjs/hot.cjs +216 -0
  29. package/server/cjs/index.cjs +33 -0
  30. package/server/esm/dev.js +42 -0
  31. package/server/esm/hot.js +207 -0
  32. package/server/esm/index.js +25 -0
  33. package/types/client/client.d.ts +13 -0
  34. package/types/client/events.d.ts +35 -0
  35. package/types/client/hot.d.ts +27 -0
  36. package/types/client/index.d.ts +4 -0
  37. package/types/client/main.d.ts +4 -0
  38. package/types/client/message.d.ts +39 -0
  39. package/types/client/ui/overlay.d.ts +19 -0
  40. package/types/client/ui/progress.d.ts +15 -0
  41. package/types/client/ui/utils/ansi/enum.d.ts +12 -0
  42. package/types/client/ui/utils/ansi/index.d.ts +18 -0
  43. package/types/client/ui/utils/ansi/interface.d.ts +68 -0
  44. package/types/client/ui/utils/ansi/regx.d.ts +6 -0
  45. package/types/client/ui/utils/ansi/utils.d.ts +6 -0
  46. package/types/client/ui/utils/index.d.ts +9 -0
  47. package/types/server/dev.d.ts +17 -0
  48. package/types/server/hot.d.ts +16 -0
  49. package/types/server/index.d.ts +42 -0
@@ -0,0 +1,207 @@
1
+ /**
2
+ * @package webpack-dev-service
3
+ * @license MIT
4
+ * @version 0.1.0
5
+ * @author nuintun <nuintun@qq.com>
6
+ * @description A koa 2 middleware for webpack development and hot reloading.
7
+ * @see https://github.com/nuintun/webpack-dev-service#readme
8
+ */
9
+
10
+ import WebSocket, { WebSocketServer } from 'ws';
11
+ import webpack from 'webpack';
12
+
13
+ /**
14
+ * @module hot
15
+ */
16
+ const WEBSOCKET_RE = /^websocket$/i;
17
+ function isObject(value) {
18
+ return Object.prototype.toString.call(value) === '[object Object]';
19
+ }
20
+ function resolveStatsOptions(compiler) {
21
+ const options = {
22
+ all: false,
23
+ hash: true,
24
+ colors: true,
25
+ errors: true,
26
+ assets: false,
27
+ builtAt: true,
28
+ warnings: true,
29
+ errorDetails: false
30
+ };
31
+ const { stats } = compiler.options;
32
+ if (isObject(stats)) {
33
+ const { warningsFilter } = stats;
34
+ if (warningsFilter !== undefined) {
35
+ options.warningsFilter = warningsFilter;
36
+ }
37
+ }
38
+ return options;
39
+ }
40
+ function normalize(path) {
41
+ const segments = [];
42
+ const parts = path.split(/[\\/]+/);
43
+ for (const segment of parts) {
44
+ switch (segment) {
45
+ case '.':
46
+ break;
47
+ case '..':
48
+ segments.pop();
49
+ break;
50
+ default:
51
+ segments.push(segment);
52
+ break;
53
+ }
54
+ }
55
+ const pathname = segments.join('/');
56
+ return pathname.startsWith('/') ? pathname : `/${pathname}`;
57
+ }
58
+ function resolveOptions(options) {
59
+ const settings = {
60
+ hmr: true,
61
+ path: '/hot',
62
+ progress: true,
63
+ ...options
64
+ };
65
+ settings.path = normalize(settings.path);
66
+ return settings;
67
+ }
68
+ function isUpgradable(context, detector) {
69
+ const { upgrade } = context.headers;
70
+ return !!upgrade && detector.test(upgrade.trim());
71
+ }
72
+ function hasProblems(problems) {
73
+ return !!problems && problems.length > 0;
74
+ }
75
+ class HotServer {
76
+ stats;
77
+ compiler;
78
+ server;
79
+ options;
80
+ name = 'webpack-hot-middleware';
81
+ logger;
82
+ constructor(compiler, options) {
83
+ this.compiler = compiler;
84
+ this.options = resolveOptions(options);
85
+ this.logger = compiler.getInfrastructureLogger(this.name);
86
+ this.server = new WebSocketServer({ path: this.options.path, noServer: true });
87
+ this.setup();
88
+ }
89
+ setup() {
90
+ this.setupWss();
91
+ this.setupHooks();
92
+ this.setupPlugins();
93
+ }
94
+ setupWss() {
95
+ const { server, logger } = this;
96
+ server.on('error', error => {
97
+ logger.error(error.message);
98
+ });
99
+ server.on('connection', client => {
100
+ if (this.stats) {
101
+ this.broadcastStats([client], this.stats);
102
+ }
103
+ });
104
+ }
105
+ setupHooks() {
106
+ const { compiler } = this;
107
+ const { hooks } = compiler;
108
+ const statsOptions = resolveStatsOptions(compiler);
109
+ hooks.done.tapAsync(this.name, (stats, next) => {
110
+ next();
111
+ this.stats = stats.toJson(statsOptions);
112
+ this.broadcastStats(this.clients(), this.stats);
113
+ });
114
+ hooks.invalid.tap(this.name, (path, builtAt) => {
115
+ this.broadcast(this.clients(), 'invalid', { path, builtAt });
116
+ });
117
+ }
118
+ setupPlugins() {
119
+ const { options, compiler } = this;
120
+ const plugins = [
121
+ new webpack.NoEmitOnErrorsPlugin(),
122
+ new webpack.DefinePlugin({
123
+ __WDS_HOT_OPTIONS__: JSON.stringify({ ...options, name: compiler.name })
124
+ })
125
+ ];
126
+ if (options.hmr) {
127
+ plugins.push(new webpack.HotModuleReplacementPlugin());
128
+ }
129
+ if (options.progress) {
130
+ let value = 0;
131
+ plugins.push(
132
+ new webpack.ProgressPlugin((percentage, status, message) => {
133
+ const nextValue = Math.floor(percentage * 100);
134
+ if (nextValue > value || nextValue === 0) {
135
+ value = nextValue;
136
+ switch (value) {
137
+ case 0:
138
+ status = 'start';
139
+ message = 'end idle';
140
+ break;
141
+ case 100:
142
+ status = 'finish';
143
+ message = 'begin idle';
144
+ break;
145
+ }
146
+ this.broadcast(this.clients(), 'progress', { status, message, value });
147
+ }
148
+ })
149
+ );
150
+ }
151
+ for (const plugin of plugins) {
152
+ plugin.apply(compiler);
153
+ }
154
+ }
155
+ clients() {
156
+ return this.server.clients;
157
+ }
158
+ upgrade(context) {
159
+ const { server } = this;
160
+ const { req: request } = context;
161
+ if (isUpgradable(context, WEBSOCKET_RE) && server.shouldHandle(request)) {
162
+ context.respond = false;
163
+ const { socket } = context;
164
+ const head = Buffer.alloc(0);
165
+ server.handleUpgrade(request, socket, head, client => {
166
+ server.emit('connection', client, request);
167
+ });
168
+ return true;
169
+ }
170
+ return false;
171
+ }
172
+ broadcast(clients, action, payload) {
173
+ for (const client of clients) {
174
+ if (client.readyState === WebSocket.OPEN) {
175
+ client.send(JSON.stringify({ action, payload }));
176
+ }
177
+ }
178
+ }
179
+ broadcastStats(clients, stats) {
180
+ if (clients.size > 0 || clients.length > 0) {
181
+ const { hash, builtAt, errors, warnings } = stats;
182
+ this.broadcast(clients, 'hash', { hash });
183
+ if (hasProblems(errors) || hasProblems(warnings)) {
184
+ this.broadcast(clients, 'problems', { errors, warnings, builtAt });
185
+ } else {
186
+ this.broadcast(clients, 'ok', { builtAt });
187
+ }
188
+ }
189
+ }
190
+ }
191
+ function hot(compiler, options = {}) {
192
+ const server = new HotServer(compiler, options);
193
+ const hotMiddleware = async (context, next) => {
194
+ if (!server.upgrade(context)) {
195
+ await next();
196
+ }
197
+ };
198
+ hotMiddleware.clients = () => {
199
+ return server.clients();
200
+ };
201
+ hotMiddleware.broadcast = (clients, action, payload) => {
202
+ server.broadcast(clients, action, payload);
203
+ };
204
+ return hotMiddleware;
205
+ }
206
+
207
+ export { hot as default };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @package webpack-dev-service
3
+ * @license MIT
4
+ * @version 0.1.0
5
+ * @author nuintun <nuintun@qq.com>
6
+ * @description A koa 2 middleware for webpack development and hot reloading.
7
+ * @see https://github.com/nuintun/webpack-dev-service#readme
8
+ */
9
+
10
+ import compose from 'koa-compose';
11
+ import dev from './dev.js';
12
+ import hot from './hot.js';
13
+
14
+ /**
15
+ * @module index
16
+ */
17
+ function server(compiler, options = {}) {
18
+ const { hot: hotOptions, ...devOptions } = options;
19
+ const devMiddleware = dev(compiler, devOptions);
20
+ if (hotOptions === false) return devMiddleware;
21
+ const hotMiddleware = hot(compiler, hotOptions);
22
+ return Object.assign(compose([devMiddleware, hotMiddleware]), devMiddleware, hotMiddleware);
23
+ }
24
+
25
+ export { server as default };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @module client
3
+ */
4
+ export interface Options {
5
+ readonly hmr: boolean;
6
+ readonly name: string;
7
+ readonly host: string;
8
+ readonly path: string;
9
+ readonly live: boolean;
10
+ readonly overlay: boolean;
11
+ readonly progress: boolean;
12
+ }
13
+ export default function createClient(options: Options): void;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @module events
3
+ */
4
+ import { Options } from './client';
5
+ import * as Message from './message';
6
+ interface Messages {
7
+ ok: Message.OK['payload'];
8
+ hash: Message.Hash['payload'];
9
+ invalid: Message.Invalid['payload'];
10
+ problems: Message.Problems['payload'];
11
+ progress: Message.Progress['payload'];
12
+ }
13
+ interface Events {
14
+ ok: (message: Messages['ok'], options: Options) => void;
15
+ hash: (message: Messages['hash'], options: Options) => void;
16
+ invalid: (message: Messages['invalid'], options: Options) => void;
17
+ problems: (message: Messages['problems'], options: Options) => void;
18
+ progress: (message: Messages['progress'], options: Options) => void;
19
+ }
20
+ /**
21
+ * @function on
22
+ * @description Add an event listener callback.
23
+ * @param event Event name.
24
+ * @param callback Event listener callback.
25
+ */
26
+ export declare function on<E extends keyof Events>(event: E, callback: Events[E]): void;
27
+ /**
28
+ * @function off
29
+ * @description Remove an event listener callback.
30
+ * @param event Event name.
31
+ * @param callback Event listener callback.
32
+ */
33
+ export declare function off<E extends keyof Events>(event: E, callback?: Events[E]): void;
34
+ export declare function emit<E extends keyof Events>(event: E, message: Messages[E], options: Options): void;
35
+ export {};
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @module hot
3
+ */
4
+ /**
5
+ * @function isUpdateAvailable
6
+ * @description Is there a newer version of this code available.
7
+ */
8
+ export declare function isUpdateAvailable(): boolean;
9
+ /**
10
+ * @function setHash
11
+ * @description Set webpack hash.
12
+ * @param value - The new hash value.
13
+ */
14
+ export declare function setHash(value: string): void;
15
+ /**
16
+ * @function setStatus
17
+ * @description Set hot status.
18
+ * @param value The new status of the hot update.
19
+ */
20
+ export declare function setStatus(value: HotUpdateStatus): void;
21
+ /**
22
+ * @function applyUpdate
23
+ * @description Apply update.
24
+ * @param hmr Whether to enable HMR.
25
+ * @param fallback Fallback function when HMR fail.
26
+ */
27
+ export declare function applyUpdate(hmr: boolean, fallback: (error?: Error) => void): void;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * @module index
3
+ */
4
+ export { off, on } from './events';
@@ -0,0 +1,4 @@
1
+ /**
2
+ * @module main
3
+ */
4
+ export {};
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @module message
3
+ */
4
+ import { StatsError } from 'webpack';
5
+ export interface Invalid {
6
+ action: 'invalid';
7
+ payload: {
8
+ path: string;
9
+ builtAt: number;
10
+ };
11
+ }
12
+ export interface Progress {
13
+ action: 'progress';
14
+ payload: {
15
+ value: number;
16
+ status: string;
17
+ message: string;
18
+ };
19
+ }
20
+ export interface Hash {
21
+ action: 'hash';
22
+ payload: {
23
+ hash: string;
24
+ };
25
+ }
26
+ export interface Problems {
27
+ action: 'problems';
28
+ payload: {
29
+ builtAt: number;
30
+ errors: StatsError[];
31
+ warnings: StatsError[];
32
+ };
33
+ }
34
+ export interface OK {
35
+ action: 'ok';
36
+ payload: {
37
+ builtAt: number;
38
+ };
39
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @module overlay
3
+ * @see https://github.com/shellscape/webpack-plugin-serve
4
+ */
5
+ import { StatsError } from 'webpack';
6
+ export default class Overlay {
7
+ private hidden;
8
+ private readonly name;
9
+ private readonly close;
10
+ private readonly aside;
11
+ private readonly errorsList;
12
+ private readonly errorsTitle;
13
+ private readonly warningsList;
14
+ private readonly warningsTitle;
15
+ constructor(name: string);
16
+ setProblems(type: 'errors' | 'warnings', problems: StatsError[]): void;
17
+ show(): void;
18
+ hide(): void;
19
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @module progress
3
+ * @see https://github.com/shellscape/webpack-plugin-serve
4
+ * @see https://www.zhangxinxu.com/wordpress/2015/07/svg-circle-loading
5
+ */
6
+ export default class Progress {
7
+ private timer?;
8
+ private hidden;
9
+ private readonly svg;
10
+ private readonly track;
11
+ constructor();
12
+ update(value: number): void;
13
+ show(): void;
14
+ hide(): void;
15
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @module enum
3
+ */
4
+ export declare const enum TokenType {
5
+ EOS = 0,
6
+ ESC = 1,
7
+ OSC = 2,
8
+ SGR = 3,
9
+ TEXT = 4,
10
+ INCESC = 5,
11
+ UNKNOWN = 6
12
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @module index
3
+ */
4
+ import { AnsiBlock, AnsiColor, AnsiStyle, Theme } from './interface';
5
+ export type { AnsiBlock, AnsiStyle };
6
+ export default class Ansi {
7
+ protected buffer: string;
8
+ protected style: AnsiStyle;
9
+ protected colors256: AnsiColor[];
10
+ protected colors16: AnsiColor[][];
11
+ constructor(theme?: Theme);
12
+ private read;
13
+ private reset;
14
+ private process;
15
+ private block;
16
+ write(text: string, callback: (block: AnsiBlock) => void): void;
17
+ flush(callback: (block: AnsiBlock) => void): void;
18
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * @module interface
3
+ */
4
+ import { TokenType } from './enum';
5
+ export type AnsiColor = [R: number, G: number, B: number];
6
+ export interface AnsiStyle {
7
+ dim: boolean;
8
+ bold: boolean;
9
+ blink: boolean;
10
+ hidden: boolean;
11
+ italic: boolean;
12
+ inverse: boolean;
13
+ overline: boolean;
14
+ underline: boolean;
15
+ strikethrough: boolean;
16
+ color: AnsiColor | null;
17
+ background: AnsiColor | null;
18
+ }
19
+ export interface AnsiBlock {
20
+ url?: string;
21
+ value: string;
22
+ style: AnsiStyle;
23
+ }
24
+ export interface EOSToken {
25
+ type: TokenType.EOS;
26
+ }
27
+ export interface ESCToken {
28
+ type: TokenType.ESC;
29
+ }
30
+ export interface OSCToken {
31
+ url: string;
32
+ value: string;
33
+ type: TokenType.OSC;
34
+ }
35
+ export interface SGRToken {
36
+ signal: string;
37
+ type: TokenType.SGR;
38
+ }
39
+ export interface TEXTToken {
40
+ value: string;
41
+ type: TokenType.TEXT;
42
+ }
43
+ export interface INCESCToken {
44
+ type: TokenType.INCESC;
45
+ }
46
+ export interface UNKNOWNToken {
47
+ type: TokenType.UNKNOWN;
48
+ }
49
+ export type BlockToken = OSCToken | TEXTToken;
50
+ export type AnsiToken = EOSToken | ESCToken | OSCToken | SGRToken | TEXTToken | INCESCToken | UNKNOWNToken;
51
+ export interface Theme {
52
+ red?: AnsiColor;
53
+ blue?: AnsiColor;
54
+ cyan?: AnsiColor;
55
+ black?: AnsiColor;
56
+ green?: AnsiColor;
57
+ white?: AnsiColor;
58
+ yellow?: AnsiColor;
59
+ magenta?: AnsiColor;
60
+ brightRed?: AnsiColor;
61
+ brightBlue?: AnsiColor;
62
+ brightCyan?: AnsiColor;
63
+ brightBlack?: AnsiColor;
64
+ brightGreen?: AnsiColor;
65
+ brightWhite?: AnsiColor;
66
+ brightYellow?: AnsiColor;
67
+ brightMagenta?: AnsiColor;
68
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @module regx
3
+ */
4
+ export declare const OSC_ST_RE: RegExp;
5
+ export declare const CSI_RE: RegExp;
6
+ export declare const OSC_RE: RegExp;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @module utils
3
+ */
4
+ import { AnsiColor } from './interface';
5
+ export declare function toUint8(uint8: number): number;
6
+ export declare function getThemeColor(defaultColor: AnsiColor, color?: AnsiColor): AnsiColor;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @module utils
3
+ */
4
+ import { AnsiBlock } from './ansi';
5
+ export declare function injectCSS(css: string, styleElement?: HTMLStyleElement): HTMLStyleElement;
6
+ export declare function appendHTML(html: string, parent?: HTMLElement): ChildNode[];
7
+ export declare function escapeHTML(text: string): string;
8
+ export declare function blockToHTML({ style, value, url }: AnsiBlock): string;
9
+ export declare function ansiToHTML(text: string): string;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @module dev
3
+ */
4
+ /// <reference types="node" />
5
+ import { AdditionalMethods, ExtendedServerResponse, IncomingMessage, Options as DevOptions } from 'webpack-dev-middleware';
6
+ import { Middleware } from 'koa';
7
+ import { Compiler } from 'webpack';
8
+ import { ServerResponse } from 'http';
9
+ export interface OutgoingMessage extends ServerResponse, ExtendedServerResponse {
10
+ send(body: any): void;
11
+ get(field: string): string;
12
+ status(statusCode: number): void;
13
+ set(field: string, value: string): void;
14
+ }
15
+ export type Options = DevOptions<IncomingMessage, OutgoingMessage>;
16
+ export type Extensions = AdditionalMethods<IncomingMessage, OutgoingMessage>;
17
+ export default function dev(compiler: Compiler, options: Options): Middleware & Extensions;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @module hot
3
+ */
4
+ import { Middleware } from 'koa';
5
+ import WebSocket from 'ws';
6
+ import { Compiler } from 'webpack';
7
+ export interface Options {
8
+ hmr?: boolean;
9
+ path?: string;
10
+ progress?: boolean;
11
+ }
12
+ export type Extensions = {
13
+ clients(): Set<WebSocket>;
14
+ broadcast<T>(clients: Set<WebSocket> | WebSocket[], action: string, payload: T): void;
15
+ };
16
+ export default function hot(compiler: Compiler, options?: Options): Middleware & Extensions;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @module index
3
+ */
4
+ import { Middleware } from 'koa';
5
+ import { Compiler } from 'webpack';
6
+ import { Extensions as DevExtensions, Options as DevOptions } from './dev';
7
+ import { Extensions as HotExtensions, Options as HotOptions } from './hot';
8
+ export type Options = DevOptions & {
9
+ hot?: false | HotOptions;
10
+ };
11
+ export type BaseMiddleware = Middleware & DevExtensions;
12
+ export type ExtendMiddleware = BaseMiddleware & HotExtensions;
13
+ /**
14
+ * @function server
15
+ * @description Create koa dev server middleware.
16
+ * @param compiler The webpack compiler instance.
17
+ */
18
+ export default function server(compiler: Compiler): ExtendMiddleware;
19
+ /**
20
+ * @function server
21
+ * @description Create koa dev server middleware.
22
+ * @param compiler The webpack compiler instance.
23
+ * @param options Options.
24
+ */
25
+ export default function server(
26
+ compiler: Compiler,
27
+ options: Options & {
28
+ hot: false;
29
+ }
30
+ ): BaseMiddleware;
31
+ /**
32
+ * @function server
33
+ * @description Create koa dev server middleware.
34
+ * @param compiler The webpack compiler instance.
35
+ * @param options Options.
36
+ */
37
+ export default function server(
38
+ compiler: Compiler,
39
+ options: Options & {
40
+ hot?: HotOptions;
41
+ }
42
+ ): ExtendMiddleware;