@roots/bud-compiler 6.13.0 → 6.14.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.d.ts CHANGED
@@ -1,11 +1,13 @@
1
1
  /**
2
- * The bud compiler interface
2
+ * @roots/bud-compiler
3
3
  *
4
4
  * @see https://bud.js.org
5
5
  * @see https://github.com/roots/bud
6
- *
7
- * @packageDocumentation
8
6
  */
9
- import './types.js';
10
- import { Compiler } from './compiler.service.js';
11
- export default Compiler;
7
+ import { Compiler } from './service.js';
8
+ declare module '@roots/bud-framework' {
9
+ interface Services {
10
+ compiler: Compiler;
11
+ }
12
+ }
13
+ export { Compiler as default };
package/lib/index.js CHANGED
@@ -1,13 +1,10 @@
1
1
  // Copyright © Roots Software Foundation LLC
2
2
  // Licensed under the MIT license.
3
3
  /**
4
- * The bud compiler interface
4
+ * @roots/bud-compiler
5
5
  *
6
6
  * @see https://bud.js.org
7
7
  * @see https://github.com/roots/bud
8
- *
9
- * @packageDocumentation
10
8
  */
11
- import './types.js';
12
- import { Compiler } from './compiler.service.js';
13
- export default Compiler;
9
+ import { Compiler } from './service.js';
10
+ export { Compiler as default };
@@ -0,0 +1,49 @@
1
+ import type { Compiler as BudCompiler } from '@roots/bud-framework';
2
+ import type { Bud } from '@roots/bud-framework';
3
+ import type { MultiCompiler, MultiStats, Stats, StatsError, Webpack } from '@roots/bud-framework/config';
4
+ import type { ErrorWithSourceFile } from '@roots/bud-support/open';
5
+ import { Service } from '@roots/bud-framework/service';
6
+ import { type BudHandler } from '@roots/bud-support/errors';
7
+ import webpack from '@roots/bud-support/webpack';
8
+ /**
9
+ * {@link BudCompiler} implementation
10
+ */
11
+ export declare class Compiler extends Service implements BudCompiler {
12
+ /**
13
+ * {@link BudCompiler.compilationStats}
14
+ */
15
+ compilationStats: BudCompiler[`compilationStats`];
16
+ /**
17
+ * {@link BudCompiler.config}
18
+ */
19
+ config: BudCompiler[`config`];
20
+ /**
21
+ * {@link BudCompiler.implementation}
22
+ */
23
+ implementation: BudCompiler[`implementation`] & typeof Webpack;
24
+ /**
25
+ * {@link BudCompiler.instance}
26
+ */
27
+ instance: BudCompiler[`instance`];
28
+ /**
29
+ * {@link BudCompiler.stats}
30
+ */
31
+ stats: BudCompiler[`stats`];
32
+ /**
33
+ * {@link BudCompiler.compile}
34
+ */
35
+ compile(bud: Bud): Promise<MultiCompiler>;
36
+ /**
37
+ * {@link BudCompiler.onError}
38
+ */
39
+ onError(error: BudHandler | webpack.WebpackError): void;
40
+ /**
41
+ * {@link BudCompiler.onStats}
42
+ */
43
+ onStats(stats: Stats & MultiStats): void;
44
+ /**
45
+ * {@link Service.register}
46
+ */
47
+ register?(bud: Bud): Promise<any>;
48
+ sourceErrors?(errors: Array<StatsError>): Array<ErrorWithSourceFile | StatsError>;
49
+ }
@@ -1,78 +1,104 @@
1
1
  import { __decorate, __metadata } from "tslib";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { cpus } from 'node:os';
4
+ import process from 'node:process';
3
5
  import { pathToFileURL } from 'node:url';
4
- import * as App from '@roots/bud-dashboard/app';
6
+ import { Error } from '@roots/bud-dashboard/components/error';
5
7
  import { Service } from '@roots/bud-framework/service';
6
8
  import { bind } from '@roots/bud-support/decorators/bind';
7
- import { BudError, CompilerError } from '@roots/bud-support/errors';
9
+ import { BudError } from '@roots/bud-support/errors';
8
10
  import { duration } from '@roots/bud-support/human-readable';
11
+ import { render } from '@roots/bud-support/ink';
9
12
  import stripAnsi from '@roots/bud-support/strip-ansi';
10
- import * as Ink from 'ink';
13
+ import webpack from '@roots/bud-support/webpack';
11
14
  /**
12
- * Wepback compilation controller class
15
+ * {@link BudCompiler} implementation
13
16
  */
14
17
  export class Compiler extends Service {
15
18
  /**
16
- * Compiler implementation
19
+ * {@link BudCompiler.compilationStats}
20
+ */
21
+ compilationStats;
22
+ /**
23
+ * {@link BudCompiler.config}
24
+ */
25
+ config = [];
26
+ /**
27
+ * {@link BudCompiler.implementation}
17
28
  */
18
29
  implementation;
19
30
  /**
20
- * Compiler instance
31
+ * {@link BudCompiler.instance}
21
32
  */
22
33
  instance;
23
34
  /**
24
- * Compilation stats
35
+ * {@link BudCompiler.stats}
25
36
  */
26
37
  stats;
27
38
  /**
28
- * Configuration
29
- */
30
- config = [];
31
- /**
32
- * Initiates compilation
39
+ * {@link BudCompiler.compile}
33
40
  */
34
- async compile() {
35
- const compilerPath = await this.app.module.resolve(`webpack`, import.meta.url);
36
- this.implementation = await this.app.module.import(compilerPath, import.meta.url);
37
- this.logger.log(`imported webpack`, this.implementation.version);
38
- this.config = !this.app.hasChildren
39
- ? [await this.app.build.make()]
40
- : await Promise.all(Object.values(this.app.children).map(async (child) => {
41
- try {
42
- return await child.build.make();
43
- }
44
- catch (error) {
45
- throw error;
46
- }
47
- }));
48
- await this.app.hooks.fire(`compiler.before`, this.app);
41
+ async compile(bud) {
42
+ this.config = !bud.hasChildren
43
+ ? [await bud.build.make()]
44
+ : await Promise.all(Object.values(bud.children).map(async (child) => await child.build.make().catch(error => {
45
+ throw error;
46
+ })));
47
+ this.config.parallelism = Math.min(cpus().length - 1, 1);
48
+ await bud.hooks.fire(`compiler.before`, bud).catch(error => {
49
+ throw error;
50
+ });
49
51
  this.logger.timeEnd(`initialize`);
50
- this.logger.await(`compilation`);
51
- this.instance = this.implementation(this.config);
52
- this.instance.hooks.done.tap(this.app.label, async (stats) => {
53
- await this.onStats(stats);
54
- await this.app.hooks.fire(`compiler.close`, this.app);
52
+ this.app.dashboard.updateStatus(`compiling`);
53
+ try {
54
+ this.instance = this.implementation(this.config);
55
+ }
56
+ catch (error) {
57
+ throw BudError.normalize(error);
58
+ }
59
+ this.instance.hooks.done.tap(bud.label, (stats) => {
60
+ this.onStats(stats);
61
+ bud.hooks.fire(`compiler.done`, bud, this.stats).catch(error => {
62
+ throw error;
63
+ });
55
64
  });
56
- await this.app.hooks.fire(`compiler.after`, this.app);
57
65
  return this.instance;
58
66
  }
59
67
  /**
60
- * Stats handler
68
+ * {@link BudCompiler.onError}
69
+ */
70
+ onError(error) {
71
+ process.exitCode = 1;
72
+ this.app.server?.appliedMiddleware?.hot?.publish({ error });
73
+ this.app.notifier?.notify({
74
+ group: this.app.label,
75
+ message: error.message,
76
+ subtitle: error.name,
77
+ });
78
+ if (`isBudError` in error) {
79
+ render(_jsx(Error, { error: error }));
80
+ }
81
+ else {
82
+ render(_jsx(Error, { error: BudError.normalize(error) }));
83
+ }
84
+ }
85
+ /**
86
+ * {@link BudCompiler.onStats}
61
87
  */
62
- async onStats(stats) {
88
+ onStats(stats) {
63
89
  const makeNoticeTitle = (child) => this.app.label !== child.name
64
90
  ? `${this.app.label} (${child.name})`
65
91
  : child.name;
66
- this.stats = stats.toJson(this.app.hooks.filter(`build.stats`));
67
- await this.app.hooks.fire(`compiler.stats`, stats);
68
- const statsUpdate = this.app.dashboard.update(stats);
92
+ this.stats = stats;
93
+ this.compilationStats = stats.toJson(statsOptions);
94
+ this.app.dashboard.updateStats(this.compilationStats);
69
95
  if (stats.hasErrors()) {
70
96
  process.exitCode = 1;
71
- this.stats.children = this.stats.children?.map(child => ({
97
+ this.compilationStats.children = this.compilationStats.children?.map(child => ({
72
98
  ...child,
73
99
  errors: this.sourceErrors(child.errors),
74
100
  }));
75
- this.stats.children
101
+ this.compilationStats.children
76
102
  ?.filter(child => child.errorsCount > 0)
77
103
  .forEach(child => {
78
104
  try {
@@ -80,11 +106,11 @@ export class Compiler extends Service {
80
106
  if (!error)
81
107
  return;
82
108
  this.app.notifier.notify({
83
- title: makeNoticeTitle(child),
84
- subtitle: error.file ? `Error in ${error.name}` : error.name,
109
+ group: `${this.app.label}-${child.name}`,
85
110
  message: stripAnsi(error.message),
86
111
  open: error.file ? pathToFileURL(error.file) : ``,
87
- group: `${this.app.label}-${child.name}`,
112
+ subtitle: error.file ? `Error in ${error.name}` : error.name,
113
+ title: makeNoticeTitle(child),
88
114
  });
89
115
  this.app.notifier.openEditor(error.file);
90
116
  }
@@ -93,18 +119,18 @@ export class Compiler extends Service {
93
119
  }
94
120
  });
95
121
  }
96
- this.stats.children
122
+ this.compilationStats.children
97
123
  ?.filter(child => child.errorsCount === 0)
98
124
  .forEach(child => {
99
125
  try {
100
126
  this.app.notifier.notify({
101
- title: makeNoticeTitle(child),
102
- subtitle: `Build successful`,
127
+ group: `${this.app.label}-${child.name}`,
103
128
  message: child.modules
104
129
  ? `${child.modules.length} modules compiled in ${duration(child.time)}`
105
130
  : `Compiled in ${duration(child.time)}`,
106
- group: `${this.app.label}-${child.name}`,
107
131
  open: this.app.server?.publicUrl.href,
132
+ subtitle: `Build successful`,
133
+ title: makeNoticeTitle(child),
108
134
  });
109
135
  this.app.notifier.openBrowser(this.app.server?.publicUrl.href);
110
136
  }
@@ -112,66 +138,38 @@ export class Compiler extends Service {
112
138
  this.logger.error(error);
113
139
  }
114
140
  });
115
- await statsUpdate;
116
141
  }
117
142
  /**
118
- * Compiler error event
143
+ * {@link Service.register}
119
144
  */
120
- async onError(error) {
121
- process.exitCode = 1;
122
- await this.app.hooks.fire(`compiler.error`, error);
123
- this.app.isDevelopment &&
124
- this.app.server.appliedMiddleware?.hot?.publish({ error });
125
- try {
126
- this.app.notifier.notify({
127
- subtitle: error.name,
128
- message: error.message,
129
- group: this.app.label,
130
- });
131
- }
132
- catch (error) {
133
- this.logger.error(error);
134
- }
135
- try {
136
- Ink.render(_jsx(App.Error, { error: new CompilerError(error.message, {
137
- props: {
138
- details: `This error was thrown by the webpack compiler itself. It is not the same as a syntax error. It is likely a missing or unresolvable build dependency.`,
139
- stack: error.stack,
140
- thrownBy: `webpack`,
141
- docs: new URL(`https://bud.js.org/`),
142
- issues: new URL(`https://github.com/roots/bud/search?q=is:issue+"compiler" in:title`),
143
- },
144
- }) }));
145
- }
146
- catch (error) {
145
+ async register(bud) {
146
+ this.implementation = await bud.module
147
+ .import(`@roots/bud-support/webpack`, import.meta.url)
148
+ .catch(error => {
147
149
  throw BudError.normalize(error);
148
- }
150
+ });
149
151
  }
150
- /**
151
- * Parse errors from webpack stats
152
- */
153
152
  sourceErrors(errors) {
154
153
  if (!errors || !errors.length)
155
154
  return [];
156
155
  try {
157
156
  const parseError = (error) => {
158
157
  let file;
159
- const modules = this.stats.children.flatMap(child => child.modules);
160
158
  const moduleIdent = error.moduleId ?? error.moduleName;
161
- const module = modules.find(module => module?.id === moduleIdent || module?.name === moduleIdent);
162
- if (!module) {
159
+ const module = this.compilationStats.children
160
+ .flatMap(child => child?.modules)
161
+ .find(module => module?.id === moduleIdent || module?.name === moduleIdent);
162
+ if (!module)
163
163
  return error;
164
- }
165
164
  if (module.nameForCondition) {
166
165
  file = module.nameForCondition;
167
166
  }
168
167
  else if (module.name) {
169
168
  file = this.app.path(`@src`, module.name);
170
169
  }
171
- if (!file) {
172
- return error;
173
- }
174
- return { ...error, name: module.name ?? error.name, file };
170
+ return !file
171
+ ? { ...error, name: module.name ?? error.name }
172
+ : { ...error, file, name: module.name ?? error.name };
175
173
  };
176
174
  return errors?.map(parseError).filter(Boolean);
177
175
  }
@@ -184,24 +182,55 @@ export class Compiler extends Service {
184
182
  __decorate([
185
183
  bind,
186
184
  __metadata("design:type", Function),
187
- __metadata("design:paramtypes", []),
185
+ __metadata("design:paramtypes", [Function]),
188
186
  __metadata("design:returntype", Promise)
189
187
  ], Compiler.prototype, "compile", null);
188
+ __decorate([
189
+ bind,
190
+ __metadata("design:type", Function),
191
+ __metadata("design:paramtypes", [Object]),
192
+ __metadata("design:returntype", void 0)
193
+ ], Compiler.prototype, "onError", null);
190
194
  __decorate([
191
195
  bind,
192
196
  __metadata("design:type", Function),
193
197
  __metadata("design:paramtypes", [Function]),
194
- __metadata("design:returntype", Promise)
198
+ __metadata("design:returntype", void 0)
195
199
  ], Compiler.prototype, "onStats", null);
196
200
  __decorate([
197
201
  bind,
198
202
  __metadata("design:type", Function),
199
- __metadata("design:paramtypes", [Error]),
203
+ __metadata("design:paramtypes", [Function]),
200
204
  __metadata("design:returntype", Promise)
201
- ], Compiler.prototype, "onError", null);
205
+ ], Compiler.prototype, "register", null);
202
206
  __decorate([
203
207
  bind,
204
208
  __metadata("design:type", Function),
205
209
  __metadata("design:paramtypes", [Array]),
206
210
  __metadata("design:returntype", Array)
207
211
  ], Compiler.prototype, "sourceErrors", null);
212
+ const statsOptions = {
213
+ all: false,
214
+ children: {
215
+ all: false,
216
+ assets: true,
217
+ cached: true,
218
+ cachedAssets: true,
219
+ cachedModules: true,
220
+ entrypoints: true,
221
+ errorDetails: false,
222
+ errors: true,
223
+ errorsCount: true,
224
+ errorStack: false,
225
+ hash: true,
226
+ modules: true,
227
+ name: true,
228
+ outputPath: true,
229
+ reasons: false,
230
+ runtime: true,
231
+ timings: true,
232
+ warnings: true,
233
+ warningsCount: true,
234
+ },
235
+ name: true,
236
+ };
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@roots/bud-compiler",
3
- "version": "6.13.0",
3
+ "version": "6.14.0",
4
4
  "description": "Compilation handler",
5
5
  "engines": {
6
6
  "node": ">=16"
7
7
  },
8
8
  "contributors": [
9
9
  {
10
- "name": "Kelly Mears",
11
10
  "email": "developers@tinypixel.dev",
11
+ "name": "Kelly Mears",
12
12
  "url": "https://github.com/kellymears"
13
13
  },
14
14
  {
15
- "name": "Ben Word",
16
15
  "email": "ben@benword.com",
16
+ "name": "Ben Word",
17
17
  "url": "https://github.com/retlehs"
18
18
  },
19
19
  {
@@ -21,8 +21,8 @@
21
21
  "url": "https://github.com/QWp6t"
22
22
  },
23
23
  {
24
- "name": "Brandon",
25
24
  "email": "brandon@tendency.me",
25
+ "name": "Brandon",
26
26
  "url": "https://github.com/Log1x"
27
27
  }
28
28
  ],
@@ -49,41 +49,29 @@
49
49
  ],
50
50
  "type": "module",
51
51
  "exports": {
52
- ".": {
53
- "import": "./lib/index.js",
54
- "default": "./lib/index.js"
55
- },
56
- "./types": {
57
- "import": "./lib/types.js",
58
- "default": "./lib/types.js"
59
- }
52
+ ".": "./lib/index.js"
60
53
  },
61
54
  "typesVersions": {
62
55
  "*": {
63
56
  ".": [
64
57
  "./lib/index.d.ts"
65
- ],
66
- "types": [
67
- "./lib/types.d.ts"
68
58
  ]
69
59
  }
70
60
  },
71
61
  "types": "./lib/index.d.ts",
72
62
  "module": "./lib/index.js",
73
63
  "devDependencies": {
74
- "@roots/bud-api": "6.13.0",
64
+ "@roots/bud-api": "6.14.0",
75
65
  "@skypack/package-check": "0.2.2",
76
- "@types/node": "18.16.12",
77
- "@types/react": "18.2.6"
66
+ "@types/node": "18.16.18",
67
+ "@types/react": "18.2.14"
78
68
  },
79
69
  "dependencies": {
80
- "@roots/bud-dashboard": "6.13.0",
81
- "@roots/bud-framework": "6.13.0",
82
- "@roots/bud-support": "6.13.0",
83
- "ink": "4.2.0",
70
+ "@roots/bud-dashboard": "6.14.0",
71
+ "@roots/bud-framework": "6.14.0",
72
+ "@roots/bud-support": "6.14.0",
84
73
  "react": "18.2.0",
85
- "tslib": "2.5.0",
86
- "webpack": "5.83.0"
74
+ "tslib": "2.6.0"
87
75
  },
88
76
  "volta": {
89
77
  "extends": "../../../package.json"
package/src/index.ts CHANGED
@@ -2,16 +2,18 @@
2
2
  // Licensed under the MIT license.
3
3
 
4
4
  /**
5
- * The bud compiler interface
5
+ * @roots/bud-compiler
6
6
  *
7
7
  * @see https://bud.js.org
8
8
  * @see https://github.com/roots/bud
9
- *
10
- * @packageDocumentation
11
9
  */
12
10
 
13
- import './types.js'
11
+ import {Compiler} from './service.js'
14
12
 
15
- import {Compiler} from './compiler.service.js'
13
+ declare module '@roots/bud-framework' {
14
+ interface Services {
15
+ compiler: Compiler
16
+ }
17
+ }
16
18
 
17
- export default Compiler
19
+ export {Compiler as default}
@@ -0,0 +1,269 @@
1
+ import type {Compiler as BudCompiler} from '@roots/bud-framework'
2
+ import type {Bud} from '@roots/bud-framework'
3
+ import type {
4
+ MultiCompiler,
5
+ MultiStats,
6
+ Stats,
7
+ StatsCompilation,
8
+ StatsError,
9
+ Webpack,
10
+ } from '@roots/bud-framework/config'
11
+ import type {
12
+ ErrorWithSourceFile,
13
+ SourceFile,
14
+ } from '@roots/bud-support/open'
15
+
16
+ import {cpus} from 'node:os'
17
+ import process from 'node:process'
18
+ import {pathToFileURL} from 'node:url'
19
+
20
+ import {Error} from '@roots/bud-dashboard/components/error'
21
+ import {Service} from '@roots/bud-framework/service'
22
+ import {bind} from '@roots/bud-support/decorators/bind'
23
+ import {BudError, type BudHandler} from '@roots/bud-support/errors'
24
+ import {duration} from '@roots/bud-support/human-readable'
25
+ import {render} from '@roots/bud-support/ink'
26
+ import stripAnsi from '@roots/bud-support/strip-ansi'
27
+ import webpack from '@roots/bud-support/webpack'
28
+
29
+ /**
30
+ * {@link BudCompiler} implementation
31
+ */
32
+ export class Compiler extends Service implements BudCompiler {
33
+ /**
34
+ * {@link BudCompiler.compilationStats}
35
+ */
36
+ public compilationStats: BudCompiler[`compilationStats`]
37
+
38
+ /**
39
+ * {@link BudCompiler.config}
40
+ */
41
+ public config: BudCompiler[`config`] = []
42
+
43
+ /**
44
+ * {@link BudCompiler.implementation}
45
+ */
46
+ public implementation: BudCompiler[`implementation`] & typeof Webpack
47
+
48
+ /**
49
+ * {@link BudCompiler.instance}
50
+ */
51
+ public instance: BudCompiler[`instance`]
52
+
53
+ /**
54
+ * {@link BudCompiler.stats}
55
+ */
56
+ public stats: BudCompiler[`stats`]
57
+
58
+ /**
59
+ * {@link BudCompiler.compile}
60
+ */
61
+ @bind
62
+ public async compile(bud: Bud): Promise<MultiCompiler> {
63
+ this.config = !bud.hasChildren
64
+ ? [await bud.build.make()]
65
+ : await Promise.all(
66
+ Object.values(bud.children).map(
67
+ async (child: Bud) =>
68
+ await child.build.make().catch(error => {
69
+ throw error
70
+ }),
71
+ ),
72
+ )
73
+ this.config.parallelism = Math.min(cpus().length - 1, 1)
74
+
75
+ await bud.hooks.fire(`compiler.before`, bud).catch(error => {
76
+ throw error
77
+ })
78
+
79
+ this.logger.timeEnd(`initialize`)
80
+ this.app.dashboard.updateStatus(`compiling`)
81
+
82
+ try {
83
+ this.instance = this.implementation(this.config)
84
+ } catch (error) {
85
+ throw BudError.normalize(error)
86
+ }
87
+
88
+ this.instance.hooks.done.tap(bud.label, (stats: any) => {
89
+ this.onStats(stats)
90
+
91
+ bud.hooks.fire(`compiler.done`, bud, this.stats).catch(error => {
92
+ throw error
93
+ })
94
+ })
95
+
96
+ return this.instance
97
+ }
98
+
99
+ /**
100
+ * {@link BudCompiler.onError}
101
+ */
102
+ @bind
103
+ public onError(error: BudHandler | webpack.WebpackError) {
104
+ process.exitCode = 1
105
+
106
+ this.app.server?.appliedMiddleware?.hot?.publish({error})
107
+
108
+ this.app.notifier?.notify({
109
+ group: this.app.label,
110
+ message: error.message,
111
+ subtitle: error.name,
112
+ })
113
+
114
+ if (`isBudError` in error) {
115
+ render(<Error error={error} />)
116
+ } else {
117
+ render(<Error error={BudError.normalize(error)} />)
118
+ }
119
+ }
120
+
121
+ /**
122
+ * {@link BudCompiler.onStats}
123
+ */
124
+ @bind
125
+ public onStats(stats: Stats & MultiStats) {
126
+ const makeNoticeTitle = (child: StatsCompilation) =>
127
+ this.app.label !== child.name
128
+ ? `${this.app.label} (${child.name})`
129
+ : child.name
130
+
131
+ this.stats = stats
132
+
133
+ this.compilationStats = stats.toJson(statsOptions)
134
+
135
+ this.app.dashboard.updateStats(this.compilationStats)
136
+
137
+ if (stats.hasErrors()) {
138
+ process.exitCode = 1
139
+
140
+ this.compilationStats.children = this.compilationStats.children?.map(
141
+ child => ({
142
+ ...child,
143
+ errors: this.sourceErrors(child.errors),
144
+ }),
145
+ )
146
+
147
+ this.compilationStats.children
148
+ ?.filter(child => child.errorsCount > 0)
149
+ .forEach(child => {
150
+ try {
151
+ const error = child.errors?.shift()
152
+ if (!error) return
153
+
154
+ this.app.notifier.notify({
155
+ group: `${this.app.label}-${child.name}`,
156
+ message: stripAnsi(error.message),
157
+ open: error.file ? pathToFileURL(error.file) : ``,
158
+ subtitle: error.file ? `Error in ${error.name}` : error.name,
159
+ title: makeNoticeTitle(child),
160
+ })
161
+ this.app.notifier.openEditor(error.file)
162
+ } catch (error) {
163
+ this.logger.error(error)
164
+ }
165
+ })
166
+ }
167
+
168
+ this.compilationStats.children
169
+ ?.filter(child => child.errorsCount === 0)
170
+ .forEach(child => {
171
+ try {
172
+ this.app.notifier.notify({
173
+ group: `${this.app.label}-${child.name}`,
174
+ message: child.modules
175
+ ? `${child.modules.length} modules compiled in ${duration(
176
+ child.time,
177
+ )}`
178
+ : `Compiled in ${duration(child.time)}`,
179
+ open: this.app.server?.publicUrl.href,
180
+ subtitle: `Build successful`,
181
+ title: makeNoticeTitle(child),
182
+ })
183
+
184
+ this.app.notifier.openBrowser(this.app.server?.publicUrl.href)
185
+ } catch (error) {
186
+ this.logger.error(error)
187
+ }
188
+ })
189
+ }
190
+
191
+ /**
192
+ * {@link Service.register}
193
+ */
194
+ @bind
195
+ public override async register?(bud: Bud): Promise<any> {
196
+ this.implementation = await bud.module
197
+ .import(`@roots/bud-support/webpack`, import.meta.url)
198
+ .catch(error => {
199
+ throw BudError.normalize(error)
200
+ })
201
+ }
202
+
203
+ @bind
204
+ public sourceErrors?(
205
+ errors: Array<StatsError>,
206
+ ): Array<ErrorWithSourceFile | StatsError> {
207
+ if (!errors || !errors.length) return []
208
+
209
+ try {
210
+ const parseError = (
211
+ error: StatsError,
212
+ ): ErrorWithSourceFile | StatsError => {
213
+ let file: SourceFile[`file`] | undefined
214
+
215
+ const moduleIdent = error.moduleId ?? error.moduleName
216
+
217
+ const module = this.compilationStats.children
218
+ .flatMap(child => child?.modules)
219
+ .find(
220
+ module =>
221
+ module?.id === moduleIdent || module?.name === moduleIdent,
222
+ )
223
+
224
+ if (!module) return error
225
+
226
+ if (module.nameForCondition) {
227
+ file = module.nameForCondition
228
+ } else if (module.name) {
229
+ file = this.app.path(`@src`, module.name)
230
+ }
231
+
232
+ return !file
233
+ ? {...error, name: module.name ?? error.name}
234
+ : {...error, file, name: module.name ?? error.name}
235
+ }
236
+
237
+ return errors?.map(parseError).filter(Boolean)
238
+ } catch (error) {
239
+ this.app.warn(`error parsing errors`, error)
240
+ return []
241
+ }
242
+ }
243
+ }
244
+
245
+ const statsOptions = {
246
+ all: false,
247
+ children: {
248
+ all: false,
249
+ assets: true,
250
+ cached: true,
251
+ cachedAssets: true,
252
+ cachedModules: true,
253
+ entrypoints: true,
254
+ errorDetails: false,
255
+ errors: true,
256
+ errorsCount: true,
257
+ errorStack: false,
258
+ hash: true,
259
+ modules: true,
260
+ name: true,
261
+ outputPath: true,
262
+ reasons: false,
263
+ runtime: true,
264
+ timings: true,
265
+ warnings: true,
266
+ warningsCount: true,
267
+ },
268
+ name: true,
269
+ }
@@ -1,42 +0,0 @@
1
- import { Service } from '@roots/bud-framework/service';
2
- import type { Compiler as Contract } from '@roots/bud-framework/services';
3
- import type { ErrorWithSourceFile } from '@roots/bud-support/open';
4
- import type webpack from '@roots/bud-support/webpack';
5
- import type { MultiCompiler, MultiStats, StatsError } from '@roots/bud-support/webpack';
6
- /**
7
- * Wepback compilation controller class
8
- */
9
- export declare class Compiler extends Service implements Contract.Service {
10
- /**
11
- * Compiler implementation
12
- */
13
- implementation: typeof webpack;
14
- /**
15
- * Compiler instance
16
- */
17
- instance: Contract.Service[`instance`];
18
- /**
19
- * Compilation stats
20
- */
21
- stats: Contract.Service[`stats`];
22
- /**
23
- * Configuration
24
- */
25
- config: Contract.Service[`config`];
26
- /**
27
- * Initiates compilation
28
- */
29
- compile(): Promise<MultiCompiler>;
30
- /**
31
- * Stats handler
32
- */
33
- onStats(stats: MultiStats): Promise<void>;
34
- /**
35
- * Compiler error event
36
- */
37
- onError(error: Error): Promise<void>;
38
- /**
39
- * Parse errors from webpack stats
40
- */
41
- sourceErrors(errors: Array<StatsError>): Array<ErrorWithSourceFile | StatsError>;
42
- }
package/lib/types.d.ts DELETED
@@ -1,4 +0,0 @@
1
- export {};
2
- /**
3
- * @package @roots/bud-compiler
4
- */
package/lib/types.js DELETED
@@ -1,4 +0,0 @@
1
- export {};
2
- /**
3
- * @package @roots/bud-compiler
4
- */
@@ -1,252 +0,0 @@
1
- import {pathToFileURL} from 'node:url'
2
-
3
- import * as App from '@roots/bud-dashboard/app'
4
- import type {Bud} from '@roots/bud-framework/bud'
5
- import {Service} from '@roots/bud-framework/service'
6
- import type {Compiler as Contract} from '@roots/bud-framework/services'
7
- import {bind} from '@roots/bud-support/decorators/bind'
8
- import {BudError, CompilerError} from '@roots/bud-support/errors'
9
- import {duration} from '@roots/bud-support/human-readable'
10
- import type {
11
- ErrorWithSourceFile,
12
- SourceFile,
13
- } from '@roots/bud-support/open'
14
- import stripAnsi from '@roots/bud-support/strip-ansi'
15
- import type webpack from '@roots/bud-support/webpack'
16
- import type {
17
- MultiCompiler,
18
- MultiStats,
19
- StatsCompilation,
20
- StatsError,
21
- } from '@roots/bud-support/webpack'
22
- import * as Ink from 'ink'
23
-
24
- /**
25
- * Wepback compilation controller class
26
- */
27
- export class Compiler extends Service implements Contract.Service {
28
- /**
29
- * Compiler implementation
30
- */
31
- public implementation: typeof webpack
32
-
33
- /**
34
- * Compiler instance
35
- */
36
- public instance: Contract.Service[`instance`]
37
-
38
- /**
39
- * Compilation stats
40
- */
41
- public stats: Contract.Service[`stats`]
42
-
43
- /**
44
- * Configuration
45
- */
46
- public config: Contract.Service[`config`] = []
47
-
48
- /**
49
- * Initiates compilation
50
- */
51
- @bind
52
- public async compile(): Promise<MultiCompiler> {
53
- const compilerPath = await this.app.module.resolve(
54
- `webpack`,
55
- import.meta.url,
56
- )
57
- this.implementation = await this.app.module.import(
58
- compilerPath,
59
- import.meta.url,
60
- )
61
- this.logger.log(`imported webpack`, this.implementation.version)
62
-
63
- this.config = !this.app.hasChildren
64
- ? [await this.app.build.make()]
65
- : await Promise.all(
66
- Object.values(this.app.children).map(async (child: Bud) => {
67
- try {
68
- return await child.build.make()
69
- } catch (error) {
70
- throw error
71
- }
72
- }),
73
- )
74
-
75
- await this.app.hooks.fire(`compiler.before`, this.app)
76
-
77
- this.logger.timeEnd(`initialize`)
78
-
79
- this.logger.await(`compilation`)
80
-
81
- this.instance = this.implementation(this.config)
82
- this.instance.hooks.done.tap(this.app.label, async (stats: any) => {
83
- await this.onStats(stats)
84
- await this.app.hooks.fire(`compiler.close`, this.app)
85
- })
86
-
87
- await this.app.hooks.fire(`compiler.after`, this.app)
88
-
89
- return this.instance
90
- }
91
-
92
- /**
93
- * Stats handler
94
- */
95
- @bind
96
- public async onStats(stats: MultiStats) {
97
- const makeNoticeTitle = (child: StatsCompilation) =>
98
- this.app.label !== child.name
99
- ? `${this.app.label} (${child.name})`
100
- : child.name
101
-
102
- this.stats = stats.toJson(this.app.hooks.filter(`build.stats`))
103
-
104
- await this.app.hooks.fire(`compiler.stats`, stats)
105
-
106
- const statsUpdate = this.app.dashboard.update(stats)
107
-
108
- if (stats.hasErrors()) {
109
- process.exitCode = 1
110
-
111
- this.stats.children = this.stats.children?.map(child => ({
112
- ...child,
113
- errors: this.sourceErrors(child.errors),
114
- }))
115
-
116
- this.stats.children
117
- ?.filter(child => child.errorsCount > 0)
118
- .forEach(child => {
119
- try {
120
- const error = child.errors?.shift()
121
- if (!error) return
122
-
123
- this.app.notifier.notify({
124
- title: makeNoticeTitle(child),
125
- subtitle: error.file ? `Error in ${error.name}` : error.name,
126
- message: stripAnsi(error.message),
127
- open: error.file ? pathToFileURL(error.file) : ``,
128
- group: `${this.app.label}-${child.name}`,
129
- })
130
- this.app.notifier.openEditor(error.file)
131
- } catch (error) {
132
- this.logger.error(error)
133
- }
134
- })
135
- }
136
-
137
- this.stats.children
138
- ?.filter(child => child.errorsCount === 0)
139
- .forEach(child => {
140
- try {
141
- this.app.notifier.notify({
142
- title: makeNoticeTitle(child),
143
- subtitle: `Build successful`,
144
- message: child.modules
145
- ? `${child.modules.length} modules compiled in ${duration(
146
- child.time,
147
- )}`
148
- : `Compiled in ${duration(child.time)}`,
149
- group: `${this.app.label}-${child.name}`,
150
- open: this.app.server?.publicUrl.href,
151
- })
152
- this.app.notifier.openBrowser(this.app.server?.publicUrl.href)
153
- } catch (error) {
154
- this.logger.error(error)
155
- }
156
- })
157
-
158
- await statsUpdate
159
- }
160
-
161
- /**
162
- * Compiler error event
163
- */
164
- @bind
165
- public async onError(error: Error) {
166
- process.exitCode = 1
167
-
168
- await this.app.hooks.fire(`compiler.error`, error)
169
-
170
- this.app.isDevelopment &&
171
- this.app.server.appliedMiddleware?.hot?.publish({error})
172
-
173
- try {
174
- this.app.notifier.notify({
175
- subtitle: error.name,
176
- message: error.message,
177
- group: this.app.label,
178
- })
179
- } catch (error) {
180
- this.logger.error(error)
181
- }
182
-
183
- try {
184
- Ink.render(
185
- <App.Error
186
- error={
187
- new CompilerError(error.message, {
188
- props: {
189
- details: `This error was thrown by the webpack compiler itself. It is not the same as a syntax error. It is likely a missing or unresolvable build dependency.`,
190
- stack: error.stack,
191
- thrownBy: `webpack`,
192
- docs: new URL(`https://bud.js.org/`),
193
- issues: new URL(
194
- `https://github.com/roots/bud/search?q=is:issue+"compiler" in:title`,
195
- ),
196
- },
197
- })
198
- }
199
- />,
200
- )
201
- } catch (error) {
202
- throw BudError.normalize(error)
203
- }
204
- }
205
-
206
- /**
207
- * Parse errors from webpack stats
208
- */
209
- @bind
210
- public sourceErrors(
211
- errors: Array<StatsError>,
212
- ): Array<ErrorWithSourceFile | StatsError> {
213
- if (!errors || !errors.length) return []
214
-
215
- try {
216
- const parseError = (
217
- error: StatsError,
218
- ): ErrorWithSourceFile | StatsError => {
219
- let file: SourceFile[`file`] | undefined
220
-
221
- const modules = this.stats.children.flatMap(child => child.modules)
222
- const moduleIdent = error.moduleId ?? error.moduleName
223
-
224
- const module = modules.find(
225
- module =>
226
- module?.id === moduleIdent || module?.name === moduleIdent,
227
- )
228
-
229
- if (!module) {
230
- return error
231
- }
232
-
233
- if (module.nameForCondition) {
234
- file = module.nameForCondition
235
- } else if (module.name) {
236
- file = this.app.path(`@src`, module.name)
237
- }
238
-
239
- if (!file) {
240
- return error
241
- }
242
-
243
- return {...error, name: module.name ?? error.name, file}
244
- }
245
-
246
- return errors?.map(parseError).filter(Boolean)
247
- } catch (error) {
248
- this.app.warn(`error parsing errors`, error)
249
- return []
250
- }
251
- }
252
- }
@@ -1,67 +0,0 @@
1
- import {Bud, factory} from '@repo/test-kit/bud'
2
- import {beforeEach, describe, expect, it, vi} from 'vitest'
3
-
4
- import Compiler from './index.js'
5
-
6
- describe(`@roots/bud-compiler`, function () {
7
- let bud: Bud
8
- let compiler: Compiler
9
-
10
- beforeEach(async () => {
11
- vi.clearAllMocks()
12
-
13
- bud = await factory({mode: `development`})
14
- compiler = new Compiler(() => bud)
15
- })
16
-
17
- it(`has compile fn`, () => {
18
- expect(compiler.compile).toBeInstanceOf(Function)
19
- })
20
-
21
- it(`should call logger.log`, async () => {
22
- const logSpy = vi.spyOn(compiler.logger, `log`)
23
- await compiler.compile()
24
- expect(logSpy).toHaveBeenCalled()
25
- })
26
-
27
- it(`should have config with array length 1`, async () => {
28
- await compiler.compile()
29
- expect(compiler.config).toHaveLength(1)
30
- })
31
-
32
- it(`should have config with array length 2 when hasChildren is true`, async () => {
33
- // @ts-ignore
34
- await bud.make(`foo`)
35
- await bud.make(`bar`)
36
-
37
- compiler.app.children = {
38
- foo: compiler.app,
39
- bar: compiler.app,
40
- }
41
-
42
- await compiler.compile()
43
- expect(compiler.config).toHaveLength(2)
44
- })
45
-
46
- it(`should set done tap`, async () => {
47
- try {
48
- await compiler.compile()
49
- expect(compiler.instance.hooks.done.tap).toHaveBeenCalledWith(
50
- `MOCK-dev-handle`,
51
- compiler.onStats,
52
- )
53
- } catch (e) {}
54
- })
55
-
56
- it(`has onStats fn`, () => {
57
- expect(compiler.onStats).toBeInstanceOf(Function)
58
- })
59
-
60
- it(`has error handler`, () => {
61
- expect(compiler.onError).toBeInstanceOf(Function)
62
- })
63
-
64
- it(`has close handler`, () => {
65
- expect(compiler.onError).toBeInstanceOf(Function)
66
- })
67
- })
package/src/types.ts DELETED
@@ -1,3 +0,0 @@
1
- /**
2
- * @package @roots/bud-compiler
3
- */