@roots/bud-compiler 2024.6.4-7 → 2024.6.5-7
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/service/formatError.d.ts +3 -0
- package/lib/service/formatError.js +54 -0
- package/lib/service/index.d.ts +5 -8
- package/lib/service/index.js +25 -126
- package/lib/service/makeNoticeTitle.d.ts +3 -0
- package/lib/service/makeNoticeTitle.js +1 -0
- package/package.json +6 -8
- package/src/service/formatError.ts +78 -0
- package/src/service/index.ts +183 -0
- package/src/service/makeNoticeTitle.ts +5 -0
- package/src/service/index.tsx +0 -300
@@ -0,0 +1,3 @@
|
|
1
|
+
import type { StatsCompilation, StatsError } from '@roots/bud-framework/config';
|
2
|
+
import type { ErrorWithSourceFile } from '@roots/bud-support/open';
|
3
|
+
export declare const makeErrorFormatter: (stats: StatsCompilation) => (error: StatsError) => ErrorWithSourceFile | StatsError;
|
@@ -0,0 +1,54 @@
|
|
1
|
+
import isNull from '@roots/bud-support/isNull';
|
2
|
+
import isString from '@roots/bud-support/isString';
|
3
|
+
import logger from '@roots/bud-support/logger';
|
4
|
+
export const makeErrorFormatter = (stats) => (error) => {
|
5
|
+
try {
|
6
|
+
let file;
|
7
|
+
let module;
|
8
|
+
const ident = error.moduleId ?? error.moduleName;
|
9
|
+
/**
|
10
|
+
* In a perfect world webpack plugins would use the
|
11
|
+
* {@link StatsError.nameForCondition} property to identify the module.
|
12
|
+
*/
|
13
|
+
if (ident && stats?.children) {
|
14
|
+
module = stats.children
|
15
|
+
.flatMap(child => child?.modules)
|
16
|
+
.find(module => [module?.id, module?.name].includes(ident));
|
17
|
+
}
|
18
|
+
/**
|
19
|
+
* If the module is not found, we try to parse the error message
|
20
|
+
*/
|
21
|
+
if (!ident && error.message?.includes(`[stylelint]`)) {
|
22
|
+
// try to get the origin of the stylelint error,
|
23
|
+
// which is contained in the second line of the error message
|
24
|
+
const unparsedOrigin = error.message?.split(`\n`)?.[1];
|
25
|
+
// if the origin is not a string or too long, we return the error as-is
|
26
|
+
if (!isString(unparsedOrigin) || unparsedOrigin.length > 100)
|
27
|
+
return error;
|
28
|
+
// extract absolute path and context relative name of module
|
29
|
+
const styleError = unparsedOrigin.match(/file:\/\/(.*)\x07(.*)\x1B]8;;/);
|
30
|
+
if (isNull(styleError))
|
31
|
+
return error;
|
32
|
+
// get parts of matched error
|
33
|
+
const [, file, name] = styleError;
|
34
|
+
// return enriched error
|
35
|
+
return { ...error, file, name, nameForCondition: file };
|
36
|
+
}
|
37
|
+
/**
|
38
|
+
* If the module is still not found, we return the error as-is
|
39
|
+
*/
|
40
|
+
if (!module)
|
41
|
+
return error;
|
42
|
+
/**
|
43
|
+
* We'll prefer the `nameForCondition` property if it exists,
|
44
|
+
* otherwise we'll use the `name` property.
|
45
|
+
*/
|
46
|
+
file = module.nameForCondition ?? module.name;
|
47
|
+
const name = module.name ?? error.name ?? `error`;
|
48
|
+
return { ...error, file, name };
|
49
|
+
}
|
50
|
+
catch (formatError) {
|
51
|
+
logger.warn(`Problem parsing errors. This probably won't break anything but please report it: https://github.com/roots/bud/issues/new`, formatError);
|
52
|
+
return error;
|
53
|
+
}
|
54
|
+
};
|
package/lib/service/index.d.ts
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
import type { Bud } from '@roots/bud-framework';
|
2
2
|
import type { Compiler as BudCompiler } from '@roots/bud-framework';
|
3
|
-
import type { MultiCompiler, MultiStats,
|
4
|
-
import type { ErrorWithSourceFile } from '@roots/bud-support/open';
|
3
|
+
import type { MultiCompiler, MultiStats, StatsError, Webpack } from '@roots/bud-framework/config';
|
5
4
|
import { Service } from '@roots/bud-framework/service';
|
6
5
|
/**
|
7
6
|
* {@link BudCompiler} implementation
|
@@ -23,14 +22,10 @@ declare class Compiler extends Service implements BudCompiler {
|
|
23
22
|
* {@link BudCompiler.stats}
|
24
23
|
*/
|
25
24
|
stats: BudCompiler[`stats`];
|
26
|
-
/**
|
27
|
-
* {@link BudCompiler.onError}
|
28
|
-
*/
|
29
|
-
onError(error: Error | undefined): void;
|
30
25
|
/**
|
31
26
|
* {@link BudCompiler.onStats}
|
32
27
|
*/
|
33
|
-
onStats(stats: MultiStats
|
28
|
+
onStats(stats: MultiStats): void;
|
34
29
|
/**
|
35
30
|
* {@link BudCompiler.compile}
|
36
31
|
*/
|
@@ -39,6 +34,8 @@ declare class Compiler extends Service implements BudCompiler {
|
|
39
34
|
* {@link Service.register}
|
40
35
|
*/
|
41
36
|
register?(bud: Bud): Promise<any>;
|
42
|
-
|
37
|
+
formatErrors(errors: Array<StatsError> | undefined): Array<({
|
38
|
+
file: string;
|
39
|
+
} & Error) | StatsError>;
|
43
40
|
}
|
44
41
|
export { Compiler as default };
|
package/lib/service/index.js
CHANGED
@@ -1,17 +1,14 @@
|
|
1
1
|
import { __decorate } from "tslib";
|
2
|
-
import { jsxDEV as _jsxDEV } from "@roots/bud-support/jsx-dev-runtime";
|
3
|
-
const _jsxFileName = "/home/runner/work/bud/bud/sources/@roots/bud-compiler/src/service/index.tsx";
|
4
2
|
import { cpus } from 'node:os';
|
5
3
|
import process from 'node:process';
|
6
4
|
import { pathToFileURL } from 'node:url';
|
7
|
-
import { Display as DisplayError } from '@roots/bud-dashboard/components/error';
|
8
5
|
import { Service } from '@roots/bud-framework/service';
|
9
6
|
import { bind } from '@roots/bud-support/decorators/bind';
|
10
|
-
import { BudError
|
11
|
-
import isNull from '@roots/bud-support/isNull';
|
7
|
+
import { BudError } from '@roots/bud-support/errors';
|
12
8
|
import isNumber from '@roots/bud-support/isNumber';
|
13
|
-
import isString from '@roots/bud-support/isString';
|
14
9
|
import stripAnsi from '@roots/bud-support/strip-ansi';
|
10
|
+
import { makeErrorFormatter } from './formatError.js';
|
11
|
+
import { makeNoticeTitle } from './makeNoticeTitle.js';
|
15
12
|
/**
|
16
13
|
* {@link BudCompiler} implementation
|
17
14
|
*/
|
@@ -24,40 +21,18 @@ class Compiler extends Service {
|
|
24
21
|
* {@link BudCompiler.implementation}
|
25
22
|
*/
|
26
23
|
implementation;
|
27
|
-
/**
|
28
|
-
* {@link BudCompiler.onError}
|
29
|
-
*/
|
30
|
-
onError(error) {
|
31
|
-
process.exitCode = 1;
|
32
|
-
if (!error)
|
33
|
-
return;
|
34
|
-
this.app.server?.appliedMiddleware?.hot?.publish({ error });
|
35
|
-
const normalized = CompilerError.normalize(error, {
|
36
|
-
thrownBy: import.meta.url,
|
37
|
-
});
|
38
|
-
normalized.details = undefined;
|
39
|
-
this.app.notifier?.notify({
|
40
|
-
group: this.app.label,
|
41
|
-
message: normalized.message,
|
42
|
-
subtitle: normalized.name,
|
43
|
-
});
|
44
|
-
this.app.context.render(_jsxDEV(DisplayError, { error: error }, void 0, false, { fileName: _jsxFileName, lineNumber: 75, columnNumber: 29 }, this));
|
45
|
-
}
|
46
24
|
/**
|
47
25
|
* {@link BudCompiler.onStats}
|
48
26
|
*/
|
49
27
|
onStats(stats) {
|
50
|
-
const makeNoticeTitle = (child) => this.app.label !== child.name
|
51
|
-
? `${this.app.label} (${child.name})`
|
52
|
-
: child.name;
|
53
28
|
this.stats = stats.toJson(statsOptions);
|
54
29
|
this.app.context.render(this.app.dashboard.render(stats));
|
55
30
|
if (stats.hasErrors()) {
|
56
31
|
process.exitCode = 1;
|
57
32
|
this.stats.children = this.stats.children?.map(child => ({
|
58
33
|
...child,
|
59
|
-
errors: child.errors
|
60
|
-
? this.
|
34
|
+
errors: child.errors
|
35
|
+
? this.formatErrors(child.errors)
|
61
36
|
: child.errors ?? [],
|
62
37
|
}));
|
63
38
|
this.stats.children
|
@@ -72,7 +47,7 @@ class Compiler extends Service {
|
|
72
47
|
message: stripAnsi(error.message),
|
73
48
|
open: error.file ? pathToFileURL(error.file) : ``,
|
74
49
|
subtitle: error.file ? `Error in ${error.name}` : error.name,
|
75
|
-
title: makeNoticeTitle(child),
|
50
|
+
title: makeNoticeTitle(this.app, child),
|
76
51
|
});
|
77
52
|
error.file && this.app.notifier.openEditor(error.file);
|
78
53
|
}
|
@@ -81,26 +56,19 @@ class Compiler extends Service {
|
|
81
56
|
}
|
82
57
|
});
|
83
58
|
}
|
84
|
-
this.stats.children
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
this.app.server?.publicUrl.href &&
|
98
|
-
this.app.context.browser &&
|
99
|
-
this.app.notifier.openBrowser(this.app.server?.publicUrl.href);
|
100
|
-
}
|
101
|
-
catch (error) {
|
102
|
-
this.logger.error(error);
|
103
|
-
}
|
59
|
+
this.stats.children?.forEach(child => {
|
60
|
+
this.app.notifier.notify({
|
61
|
+
group: `${this.app.label}-${child.name}`,
|
62
|
+
message: child.modules
|
63
|
+
? `${child.modules.length} modules compiled`
|
64
|
+
: `Modules compiled successfully`,
|
65
|
+
open: this.app.server?.publicUrl.href,
|
66
|
+
subtitle: `Build successful`,
|
67
|
+
title: makeNoticeTitle(this.app, child),
|
68
|
+
});
|
69
|
+
this.app.server?.publicUrl.href &&
|
70
|
+
this.app.context.browser &&
|
71
|
+
this.app.notifier.openBrowser(this.app.server?.publicUrl.href);
|
104
72
|
});
|
105
73
|
}
|
106
74
|
/**
|
@@ -109,9 +77,7 @@ class Compiler extends Service {
|
|
109
77
|
async compile(bud) {
|
110
78
|
const config = !bud.hasChildren
|
111
79
|
? [await bud.build.make()]
|
112
|
-
: await Promise.all(Object.values(bud.children).map(async (child) => await child.build.make()
|
113
|
-
throw error;
|
114
|
-
})));
|
80
|
+
: await Promise.all(Object.values(bud.children).map(async (child) => await child.build.make()));
|
115
81
|
this.config = config?.filter(Boolean);
|
116
82
|
if (this.config.length > 1) {
|
117
83
|
this.config.parallelism = Math.max(cpus().length - 1, 1);
|
@@ -120,14 +86,9 @@ class Compiler extends Service {
|
|
120
86
|
await bud.hooks.fire(`compiler.before`, bud).catch(error => {
|
121
87
|
throw error;
|
122
88
|
});
|
123
|
-
this.logger.timeEnd(`initialize`);
|
124
89
|
this.instance = this.implementation(this.config);
|
125
|
-
this.instance.hooks.done.tap(bud.label,
|
126
|
-
|
127
|
-
bud.hooks
|
128
|
-
.fire(`compiler.done`, bud, this.stats)
|
129
|
-
.catch(this.app.catch);
|
130
|
-
});
|
90
|
+
this.instance.hooks.done.tap(bud.label, this.onStats);
|
91
|
+
this.instance.hooks.done.tap(`${bud.label}-compiler.done`, stats => bud.hooks.fire(`compiler.done`, bud, stats));
|
131
92
|
return this.instance;
|
132
93
|
}
|
133
94
|
/**
|
@@ -140,72 +101,10 @@ class Compiler extends Service {
|
|
140
101
|
throw BudError.normalize(error);
|
141
102
|
});
|
142
103
|
}
|
143
|
-
|
144
|
-
|
145
|
-
return [];
|
146
|
-
try {
|
147
|
-
return errors
|
148
|
-
?.map((error) => {
|
149
|
-
let file;
|
150
|
-
let module;
|
151
|
-
const ident = error.moduleId ?? error.moduleName;
|
152
|
-
/**
|
153
|
-
* In a perfect world webpack plugins would use the
|
154
|
-
* `nameForCondition` property to identify the module.
|
155
|
-
*/
|
156
|
-
if (ident && this.stats?.children) {
|
157
|
-
module = this.stats.children
|
158
|
-
.flatMap(child => child?.modules)
|
159
|
-
.find(module => [module?.id, module?.name].includes(ident));
|
160
|
-
}
|
161
|
-
/**
|
162
|
-
* If the module is not found, we try to parse the error message
|
163
|
-
*/
|
164
|
-
if (!ident && error.message?.includes(`[stylelint]`)) {
|
165
|
-
// try to get the origin of the stylelint error,
|
166
|
-
// which is contained in the second line of the error message
|
167
|
-
const unparsedOrigin = error.message?.split(`\n`)?.[1];
|
168
|
-
// if the origin is not a string or too long, we return the error as-is
|
169
|
-
if (!isString(unparsedOrigin) || unparsedOrigin.length > 100)
|
170
|
-
return error;
|
171
|
-
// extract absolute path and context relative name of module
|
172
|
-
const styleError = unparsedOrigin.match(/file:\/\/(.*)\x07(.*)\x1B]8;;/);
|
173
|
-
if (isNull(styleError))
|
174
|
-
return error;
|
175
|
-
// get parts of matched error
|
176
|
-
const [, file, name] = styleError;
|
177
|
-
// return enriched error
|
178
|
-
return { ...error, file, name, nameForCondition: file };
|
179
|
-
}
|
180
|
-
/**
|
181
|
-
* If the module is still not found, we return the error as-is
|
182
|
-
*/
|
183
|
-
if (!module)
|
184
|
-
return error;
|
185
|
-
/**
|
186
|
-
* We'll prefer the `nameForCondition` property if it exists,
|
187
|
-
* otherwise we'll use the `name` property.
|
188
|
-
*/
|
189
|
-
if (module.nameForCondition) {
|
190
|
-
file = module.nameForCondition;
|
191
|
-
}
|
192
|
-
else if (module.name) {
|
193
|
-
file = this.app.path(`@src`, module.name);
|
194
|
-
}
|
195
|
-
const name = module.name ?? error.name ?? `error`;
|
196
|
-
return { ...error, file, name };
|
197
|
-
})
|
198
|
-
.filter(Boolean);
|
199
|
-
}
|
200
|
-
catch (error) {
|
201
|
-
this.logger.warn(`Problem parsing errors. This probably won't break anything but please report it: https://github.com/roots/bud/issues/new`, error);
|
202
|
-
return errors;
|
203
|
-
}
|
104
|
+
formatErrors(errors) {
|
105
|
+
return (errors?.map(makeErrorFormatter(this.stats)).filter(Boolean) ?? []);
|
204
106
|
}
|
205
107
|
}
|
206
|
-
__decorate([
|
207
|
-
bind
|
208
|
-
], Compiler.prototype, "onError", null);
|
209
108
|
__decorate([
|
210
109
|
bind
|
211
110
|
], Compiler.prototype, "onStats", null);
|
@@ -217,7 +116,7 @@ __decorate([
|
|
217
116
|
], Compiler.prototype, "register", null);
|
218
117
|
__decorate([
|
219
118
|
bind
|
220
|
-
], Compiler.prototype, "
|
119
|
+
], Compiler.prototype, "formatErrors", null);
|
221
120
|
const statsOptions = {
|
222
121
|
all: false,
|
223
122
|
children: {
|
@@ -0,0 +1 @@
|
|
1
|
+
export const makeNoticeTitle = (bud, child) => bud.label !== child.name ? `${bud.label} (${child.name})` : child.name;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@roots/bud-compiler",
|
3
|
-
"version": "2024.6.
|
3
|
+
"version": "2024.6.5-7",
|
4
4
|
"description": "Compilation handler",
|
5
5
|
"engines": {
|
6
6
|
"node": ">=16"
|
@@ -65,16 +65,14 @@
|
|
65
65
|
"types": "./lib/index.d.ts",
|
66
66
|
"module": "./lib/index.js",
|
67
67
|
"devDependencies": {
|
68
|
-
"@roots/bud-api": "2024.6.
|
68
|
+
"@roots/bud-api": "2024.6.5-7",
|
69
69
|
"@skypack/package-check": "0.2.2",
|
70
|
-
"@types/node": "20.12.8"
|
71
|
-
"@types/react": "18.3.1"
|
70
|
+
"@types/node": "20.12.8"
|
72
71
|
},
|
73
72
|
"dependencies": {
|
74
|
-
"@roots/bud-dashboard": "2024.6.
|
75
|
-
"@roots/bud-framework": "2024.6.
|
76
|
-
"@roots/bud-support": "2024.6.
|
77
|
-
"react": "18.3.1",
|
73
|
+
"@roots/bud-dashboard": "2024.6.5-7",
|
74
|
+
"@roots/bud-framework": "2024.6.5-7",
|
75
|
+
"@roots/bud-support": "2024.6.5-7",
|
78
76
|
"tslib": "2.6.2"
|
79
77
|
},
|
80
78
|
"volta": {
|
@@ -0,0 +1,78 @@
|
|
1
|
+
import type {
|
2
|
+
StatsCompilation,
|
3
|
+
StatsError,
|
4
|
+
StatsModule,
|
5
|
+
} from '@roots/bud-framework/config'
|
6
|
+
import type {
|
7
|
+
ErrorWithSourceFile,
|
8
|
+
SourceFile,
|
9
|
+
} from '@roots/bud-support/open'
|
10
|
+
|
11
|
+
import isNull from '@roots/bud-support/isNull'
|
12
|
+
import isString from '@roots/bud-support/isString'
|
13
|
+
import logger from '@roots/bud-support/logger'
|
14
|
+
|
15
|
+
export const makeErrorFormatter =
|
16
|
+
(stats: StatsCompilation) =>
|
17
|
+
(error: StatsError): ErrorWithSourceFile | StatsError => {
|
18
|
+
try {
|
19
|
+
let file: SourceFile[`file`] | undefined
|
20
|
+
let module: StatsModule | undefined
|
21
|
+
|
22
|
+
const ident = error.moduleId ?? error.moduleName
|
23
|
+
|
24
|
+
/**
|
25
|
+
* In a perfect world webpack plugins would use the
|
26
|
+
* {@link StatsError.nameForCondition} property to identify the module.
|
27
|
+
*/
|
28
|
+
if (ident && stats?.children) {
|
29
|
+
module = stats.children
|
30
|
+
.flatMap(child => child?.modules)
|
31
|
+
.find(module => [module?.id, module?.name].includes(ident))
|
32
|
+
}
|
33
|
+
|
34
|
+
/**
|
35
|
+
* If the module is not found, we try to parse the error message
|
36
|
+
*/
|
37
|
+
if (!ident && error.message?.includes(`[stylelint]`)) {
|
38
|
+
// try to get the origin of the stylelint error,
|
39
|
+
// which is contained in the second line of the error message
|
40
|
+
const unparsedOrigin = error.message?.split(`\n`)?.[1]
|
41
|
+
|
42
|
+
// if the origin is not a string or too long, we return the error as-is
|
43
|
+
if (!isString(unparsedOrigin) || unparsedOrigin.length > 100)
|
44
|
+
return error
|
45
|
+
|
46
|
+
// extract absolute path and context relative name of module
|
47
|
+
const styleError = unparsedOrigin.match(
|
48
|
+
/file:\/\/(.*)\x07(.*)\x1B]8;;/,
|
49
|
+
)
|
50
|
+
if (isNull(styleError)) return error
|
51
|
+
|
52
|
+
// get parts of matched error
|
53
|
+
const [, file, name] = styleError
|
54
|
+
// return enriched error
|
55
|
+
return {...error, file, name, nameForCondition: file}
|
56
|
+
}
|
57
|
+
|
58
|
+
/**
|
59
|
+
* If the module is still not found, we return the error as-is
|
60
|
+
*/
|
61
|
+
if (!module) return error
|
62
|
+
|
63
|
+
/**
|
64
|
+
* We'll prefer the `nameForCondition` property if it exists,
|
65
|
+
* otherwise we'll use the `name` property.
|
66
|
+
*/
|
67
|
+
file = module.nameForCondition ?? module.name
|
68
|
+
|
69
|
+
const name = module.name ?? error.name ?? `error`
|
70
|
+
return {...error, file, name}
|
71
|
+
} catch (formatError) {
|
72
|
+
logger.warn(
|
73
|
+
`Problem parsing errors. This probably won't break anything but please report it: https://github.com/roots/bud/issues/new`,
|
74
|
+
formatError,
|
75
|
+
)
|
76
|
+
return error
|
77
|
+
}
|
78
|
+
}
|
@@ -0,0 +1,183 @@
|
|
1
|
+
import type {Bud} from '@roots/bud-framework'
|
2
|
+
import type {Compiler as BudCompiler} from '@roots/bud-framework'
|
3
|
+
import type {
|
4
|
+
MultiCompiler,
|
5
|
+
MultiStats,
|
6
|
+
StatsError,
|
7
|
+
Webpack,
|
8
|
+
} from '@roots/bud-framework/config'
|
9
|
+
|
10
|
+
import {cpus} from 'node:os'
|
11
|
+
import process from 'node:process'
|
12
|
+
import {pathToFileURL} from 'node:url'
|
13
|
+
|
14
|
+
import {Service} from '@roots/bud-framework/service'
|
15
|
+
import {bind} from '@roots/bud-support/decorators/bind'
|
16
|
+
import {BudError} from '@roots/bud-support/errors'
|
17
|
+
import isNumber from '@roots/bud-support/isNumber'
|
18
|
+
import stripAnsi from '@roots/bud-support/strip-ansi'
|
19
|
+
|
20
|
+
import {makeErrorFormatter} from './formatError.js'
|
21
|
+
import {makeNoticeTitle} from './makeNoticeTitle.js'
|
22
|
+
|
23
|
+
/**
|
24
|
+
* {@link BudCompiler} implementation
|
25
|
+
*/
|
26
|
+
class Compiler extends Service implements BudCompiler {
|
27
|
+
/**
|
28
|
+
* {@link BudCompiler.config}
|
29
|
+
*/
|
30
|
+
public config: BudCompiler[`config`] = []
|
31
|
+
|
32
|
+
/**
|
33
|
+
* {@link BudCompiler.implementation}
|
34
|
+
*/
|
35
|
+
public implementation: BudCompiler[`implementation`] & typeof Webpack
|
36
|
+
|
37
|
+
/**
|
38
|
+
* {@link BudCompiler.instance}
|
39
|
+
*/
|
40
|
+
public declare instance: BudCompiler[`instance`]
|
41
|
+
|
42
|
+
/**
|
43
|
+
* {@link BudCompiler.stats}
|
44
|
+
*/
|
45
|
+
public declare stats: BudCompiler[`stats`]
|
46
|
+
|
47
|
+
/**
|
48
|
+
* {@link BudCompiler.onStats}
|
49
|
+
*/
|
50
|
+
@bind
|
51
|
+
public onStats(stats: MultiStats) {
|
52
|
+
this.stats = stats.toJson(statsOptions)
|
53
|
+
this.app.context.render(this.app.dashboard.render(stats))
|
54
|
+
|
55
|
+
if (stats.hasErrors()) {
|
56
|
+
process.exitCode = 1
|
57
|
+
|
58
|
+
this.stats.children = this.stats.children?.map(child => ({
|
59
|
+
...child,
|
60
|
+
errors: child.errors
|
61
|
+
? this.formatErrors(child.errors)
|
62
|
+
: child.errors ?? [],
|
63
|
+
}))
|
64
|
+
|
65
|
+
this.stats.children
|
66
|
+
?.filter(
|
67
|
+
child => isNumber(child.errorsCount) && child.errorsCount > 0,
|
68
|
+
)
|
69
|
+
.forEach(child => {
|
70
|
+
try {
|
71
|
+
const error = child.errors?.shift()
|
72
|
+
if (!error) return
|
73
|
+
|
74
|
+
this.app.notifier.notify({
|
75
|
+
group: `${this.app.label}-${child.name}`,
|
76
|
+
message: stripAnsi(error.message),
|
77
|
+
open: error.file ? pathToFileURL(error.file) : ``,
|
78
|
+
subtitle: error.file ? `Error in ${error.name}` : error.name,
|
79
|
+
title: makeNoticeTitle(this.app, child),
|
80
|
+
})
|
81
|
+
|
82
|
+
error.file && this.app.notifier.openEditor(error.file)
|
83
|
+
} catch (error) {
|
84
|
+
this.logger.error(error)
|
85
|
+
}
|
86
|
+
})
|
87
|
+
}
|
88
|
+
|
89
|
+
this.stats.children?.forEach(child => {
|
90
|
+
this.app.notifier.notify({
|
91
|
+
group: `${this.app.label}-${child.name}`,
|
92
|
+
message: child.modules
|
93
|
+
? `${child.modules.length} modules compiled`
|
94
|
+
: `Modules compiled successfully`,
|
95
|
+
open: this.app.server?.publicUrl.href,
|
96
|
+
subtitle: `Build successful`,
|
97
|
+
title: makeNoticeTitle(this.app, child),
|
98
|
+
})
|
99
|
+
|
100
|
+
this.app.server?.publicUrl.href &&
|
101
|
+
this.app.context.browser &&
|
102
|
+
this.app.notifier.openBrowser(this.app.server?.publicUrl.href)
|
103
|
+
})
|
104
|
+
}
|
105
|
+
|
106
|
+
/**
|
107
|
+
* {@link BudCompiler.compile}
|
108
|
+
*/
|
109
|
+
@bind
|
110
|
+
public async compile(bud: Bud): Promise<MultiCompiler> {
|
111
|
+
const config = !bud.hasChildren
|
112
|
+
? [await bud.build.make()]
|
113
|
+
: await Promise.all(
|
114
|
+
Object.values(bud.children).map(
|
115
|
+
async (child: Bud) => await child.build.make(),
|
116
|
+
),
|
117
|
+
)
|
118
|
+
|
119
|
+
this.config = config?.filter(Boolean)
|
120
|
+
|
121
|
+
if (this.config.length > 1) {
|
122
|
+
this.config.parallelism = Math.max(cpus().length - 1, 1)
|
123
|
+
this.logger.info(`parallel compilations: ${this.config.parallelism}`)
|
124
|
+
}
|
125
|
+
|
126
|
+
await bud.hooks.fire(`compiler.before`, bud).catch(error => {
|
127
|
+
throw error
|
128
|
+
})
|
129
|
+
|
130
|
+
this.instance = this.implementation(this.config)
|
131
|
+
this.instance.hooks.done.tap(bud.label, this.onStats)
|
132
|
+
this.instance.hooks.done.tap(`${bud.label}-compiler.done`, stats =>
|
133
|
+
bud.hooks.fire(`compiler.done`, bud, stats),
|
134
|
+
)
|
135
|
+
|
136
|
+
return this.instance
|
137
|
+
}
|
138
|
+
|
139
|
+
/**
|
140
|
+
* {@link Service.register}
|
141
|
+
*/
|
142
|
+
@bind
|
143
|
+
public override async register?(bud: Bud): Promise<any> {
|
144
|
+
this.implementation = await bud.module
|
145
|
+
.import(`@roots/bud-support/webpack`, import.meta.url)
|
146
|
+
.catch(error => {
|
147
|
+
throw BudError.normalize(error)
|
148
|
+
})
|
149
|
+
}
|
150
|
+
|
151
|
+
@bind
|
152
|
+
public formatErrors(
|
153
|
+
errors: Array<StatsError> | undefined,
|
154
|
+
): Array<({file: string} & Error) | StatsError> {
|
155
|
+
return (
|
156
|
+
errors?.map(makeErrorFormatter(this.stats)).filter(Boolean) ?? []
|
157
|
+
)
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
161
|
+
const statsOptions = {
|
162
|
+
all: false,
|
163
|
+
children: {
|
164
|
+
all: false,
|
165
|
+
assets: true,
|
166
|
+
cached: true,
|
167
|
+
cachedAssets: true,
|
168
|
+
cachedModules: true,
|
169
|
+
entrypoints: true,
|
170
|
+
errorDetails: false,
|
171
|
+
errors: true,
|
172
|
+
errorsCount: true,
|
173
|
+
hash: true,
|
174
|
+
modules: true,
|
175
|
+
name: true,
|
176
|
+
outputPath: true,
|
177
|
+
timings: true,
|
178
|
+
warnings: true,
|
179
|
+
warningsCount: true,
|
180
|
+
},
|
181
|
+
}
|
182
|
+
|
183
|
+
export {Compiler as default}
|
package/src/service/index.tsx
DELETED
@@ -1,300 +0,0 @@
|
|
1
|
-
import type {Bud} from '@roots/bud-framework'
|
2
|
-
import type {Compiler as BudCompiler} 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 {Display as DisplayError} 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, CompilerError} from '@roots/bud-support/errors'
|
24
|
-
import isNull from '@roots/bud-support/isNull'
|
25
|
-
import isNumber from '@roots/bud-support/isNumber'
|
26
|
-
import isString from '@roots/bud-support/isString'
|
27
|
-
import stripAnsi from '@roots/bud-support/strip-ansi'
|
28
|
-
|
29
|
-
/**
|
30
|
-
* {@link BudCompiler} implementation
|
31
|
-
*/
|
32
|
-
class Compiler extends Service implements BudCompiler {
|
33
|
-
/**
|
34
|
-
* {@link BudCompiler.config}
|
35
|
-
*/
|
36
|
-
public config: BudCompiler[`config`] = []
|
37
|
-
|
38
|
-
/**
|
39
|
-
* {@link BudCompiler.implementation}
|
40
|
-
*/
|
41
|
-
public implementation: BudCompiler[`implementation`] & typeof Webpack
|
42
|
-
|
43
|
-
/**
|
44
|
-
* {@link BudCompiler.instance}
|
45
|
-
*/
|
46
|
-
public declare instance: BudCompiler[`instance`]
|
47
|
-
|
48
|
-
/**
|
49
|
-
* {@link BudCompiler.stats}
|
50
|
-
*/
|
51
|
-
public declare stats: BudCompiler[`stats`]
|
52
|
-
|
53
|
-
/**
|
54
|
-
* {@link BudCompiler.onError}
|
55
|
-
*/
|
56
|
-
@bind
|
57
|
-
public onError(error: Error | undefined) {
|
58
|
-
process.exitCode = 1
|
59
|
-
if (!error) return
|
60
|
-
|
61
|
-
this.app.server?.appliedMiddleware?.hot?.publish({error})
|
62
|
-
|
63
|
-
const normalized = CompilerError.normalize(error, {
|
64
|
-
thrownBy: import.meta.url,
|
65
|
-
})
|
66
|
-
|
67
|
-
normalized.details = undefined
|
68
|
-
|
69
|
-
this.app.notifier?.notify({
|
70
|
-
group: this.app.label,
|
71
|
-
message: normalized.message,
|
72
|
-
subtitle: normalized.name,
|
73
|
-
})
|
74
|
-
|
75
|
-
this.app.context.render(<DisplayError error={error} />)
|
76
|
-
}
|
77
|
-
|
78
|
-
/**
|
79
|
-
* {@link BudCompiler.onStats}
|
80
|
-
*/
|
81
|
-
@bind
|
82
|
-
public onStats(stats: MultiStats & Stats) {
|
83
|
-
const makeNoticeTitle = (child: StatsCompilation) =>
|
84
|
-
this.app.label !== child.name
|
85
|
-
? `${this.app.label} (${child.name})`
|
86
|
-
: child.name
|
87
|
-
|
88
|
-
this.stats = stats.toJson(statsOptions)
|
89
|
-
this.app.context.render(this.app.dashboard.render(stats))
|
90
|
-
|
91
|
-
if (stats.hasErrors()) {
|
92
|
-
process.exitCode = 1
|
93
|
-
|
94
|
-
this.stats.children = this.stats.children?.map(child => ({
|
95
|
-
...child,
|
96
|
-
errors:
|
97
|
-
child.errors && this.sourceErrors
|
98
|
-
? this.sourceErrors(child.errors)
|
99
|
-
: child.errors ?? [],
|
100
|
-
}))
|
101
|
-
|
102
|
-
this.stats.children
|
103
|
-
?.filter(
|
104
|
-
child => isNumber(child.errorsCount) && child.errorsCount > 0,
|
105
|
-
)
|
106
|
-
.forEach(child => {
|
107
|
-
try {
|
108
|
-
const error = child.errors?.shift()
|
109
|
-
if (!error) return
|
110
|
-
|
111
|
-
this.app.notifier.notify({
|
112
|
-
group: `${this.app.label}-${child.name}`,
|
113
|
-
message: stripAnsi(error.message),
|
114
|
-
open: error.file ? pathToFileURL(error.file) : ``,
|
115
|
-
subtitle: error.file ? `Error in ${error.name}` : error.name,
|
116
|
-
title: makeNoticeTitle(child),
|
117
|
-
})
|
118
|
-
|
119
|
-
error.file && this.app.notifier.openEditor(error.file)
|
120
|
-
} catch (error) {
|
121
|
-
this.logger.error(error)
|
122
|
-
}
|
123
|
-
})
|
124
|
-
}
|
125
|
-
|
126
|
-
this.stats.children
|
127
|
-
?.filter(child => child.errorsCount === 0)
|
128
|
-
.forEach(child => {
|
129
|
-
try {
|
130
|
-
this.app.notifier.notify({
|
131
|
-
group: `${this.app.label}-${child.name}`,
|
132
|
-
message: child.modules
|
133
|
-
? `${child.modules.length} modules compiled`
|
134
|
-
: `Modules compiled successfully`,
|
135
|
-
open: this.app.server?.publicUrl.href,
|
136
|
-
subtitle: `Build successful`,
|
137
|
-
title: makeNoticeTitle(child),
|
138
|
-
})
|
139
|
-
|
140
|
-
this.app.server?.publicUrl.href &&
|
141
|
-
this.app.context.browser &&
|
142
|
-
this.app.notifier.openBrowser(this.app.server?.publicUrl.href)
|
143
|
-
} catch (error) {
|
144
|
-
this.logger.error(error)
|
145
|
-
}
|
146
|
-
})
|
147
|
-
}
|
148
|
-
/**
|
149
|
-
* {@link BudCompiler.compile}
|
150
|
-
*/
|
151
|
-
@bind
|
152
|
-
public async compile(bud: Bud): Promise<MultiCompiler> {
|
153
|
-
const config = !bud.hasChildren
|
154
|
-
? [await bud.build.make()]
|
155
|
-
: await Promise.all(
|
156
|
-
Object.values(bud.children).map(
|
157
|
-
async (child: Bud) =>
|
158
|
-
await child.build.make().catch(error => {
|
159
|
-
throw error
|
160
|
-
}),
|
161
|
-
),
|
162
|
-
)
|
163
|
-
|
164
|
-
this.config = config?.filter(Boolean)
|
165
|
-
|
166
|
-
if (this.config.length > 1) {
|
167
|
-
this.config.parallelism = Math.max(cpus().length - 1, 1)
|
168
|
-
this.logger.info(`parallel compilations: ${this.config.parallelism}`)
|
169
|
-
}
|
170
|
-
|
171
|
-
await bud.hooks.fire(`compiler.before`, bud).catch(error => {
|
172
|
-
throw error
|
173
|
-
})
|
174
|
-
|
175
|
-
this.logger.timeEnd(`initialize`)
|
176
|
-
|
177
|
-
this.instance = this.implementation(this.config)
|
178
|
-
|
179
|
-
this.instance.hooks.done.tap(bud.label, (stats: any) => {
|
180
|
-
this.onStats(stats)
|
181
|
-
bud.hooks
|
182
|
-
.fire(`compiler.done`, bud, this.stats)
|
183
|
-
.catch(this.app.catch)
|
184
|
-
})
|
185
|
-
|
186
|
-
return this.instance
|
187
|
-
}
|
188
|
-
|
189
|
-
/**
|
190
|
-
* {@link Service.register}
|
191
|
-
*/
|
192
|
-
@bind
|
193
|
-
public override async register?(bud: Bud): Promise<any> {
|
194
|
-
this.implementation = await bud.module
|
195
|
-
.import(`@roots/bud-support/webpack`, import.meta.url)
|
196
|
-
.catch(error => {
|
197
|
-
throw BudError.normalize(error)
|
198
|
-
})
|
199
|
-
}
|
200
|
-
|
201
|
-
@bind
|
202
|
-
public sourceErrors?(
|
203
|
-
errors: Array<StatsError> | undefined,
|
204
|
-
): Array<ErrorWithSourceFile | StatsError> {
|
205
|
-
if (!errors || !errors.length) return []
|
206
|
-
|
207
|
-
try {
|
208
|
-
return errors
|
209
|
-
?.map((error: StatsError): ErrorWithSourceFile | StatsError => {
|
210
|
-
let file: SourceFile[`file`] | undefined
|
211
|
-
let module: undefined | Webpack.StatsModule
|
212
|
-
|
213
|
-
const ident = error.moduleId ?? error.moduleName
|
214
|
-
|
215
|
-
/**
|
216
|
-
* In a perfect world webpack plugins would use the
|
217
|
-
* `nameForCondition` property to identify the module.
|
218
|
-
*/
|
219
|
-
if (ident && this.stats?.children) {
|
220
|
-
module = this.stats.children
|
221
|
-
.flatMap(child => child?.modules)
|
222
|
-
.find(module => [module?.id, module?.name].includes(ident))
|
223
|
-
}
|
224
|
-
|
225
|
-
/**
|
226
|
-
* If the module is not found, we try to parse the error message
|
227
|
-
*/
|
228
|
-
if (!ident && error.message?.includes(`[stylelint]`)) {
|
229
|
-
// try to get the origin of the stylelint error,
|
230
|
-
// which is contained in the second line of the error message
|
231
|
-
const unparsedOrigin = error.message?.split(`\n`)?.[1]
|
232
|
-
|
233
|
-
// if the origin is not a string or too long, we return the error as-is
|
234
|
-
if (!isString(unparsedOrigin) || unparsedOrigin.length > 100)
|
235
|
-
return error
|
236
|
-
|
237
|
-
// extract absolute path and context relative name of module
|
238
|
-
const styleError = unparsedOrigin.match(
|
239
|
-
/file:\/\/(.*)\x07(.*)\x1B]8;;/,
|
240
|
-
)
|
241
|
-
if (isNull(styleError)) return error
|
242
|
-
|
243
|
-
// get parts of matched error
|
244
|
-
const [, file, name] = styleError
|
245
|
-
// return enriched error
|
246
|
-
return {...error, file, name, nameForCondition: file}
|
247
|
-
}
|
248
|
-
|
249
|
-
/**
|
250
|
-
* If the module is still not found, we return the error as-is
|
251
|
-
*/
|
252
|
-
if (!module) return error
|
253
|
-
|
254
|
-
/**
|
255
|
-
* We'll prefer the `nameForCondition` property if it exists,
|
256
|
-
* otherwise we'll use the `name` property.
|
257
|
-
*/
|
258
|
-
if (module.nameForCondition) {
|
259
|
-
file = module.nameForCondition
|
260
|
-
} else if (module.name) {
|
261
|
-
file = this.app.path(`@src`, module.name)
|
262
|
-
}
|
263
|
-
|
264
|
-
const name = module.name ?? error.name ?? `error`
|
265
|
-
return {...error, file, name}
|
266
|
-
})
|
267
|
-
.filter(Boolean)
|
268
|
-
} catch (error) {
|
269
|
-
this.logger.warn(
|
270
|
-
`Problem parsing errors. This probably won't break anything but please report it: https://github.com/roots/bud/issues/new`,
|
271
|
-
error,
|
272
|
-
)
|
273
|
-
return errors
|
274
|
-
}
|
275
|
-
}
|
276
|
-
}
|
277
|
-
|
278
|
-
const statsOptions = {
|
279
|
-
all: false,
|
280
|
-
children: {
|
281
|
-
all: false,
|
282
|
-
assets: true,
|
283
|
-
cached: true,
|
284
|
-
cachedAssets: true,
|
285
|
-
cachedModules: true,
|
286
|
-
entrypoints: true,
|
287
|
-
errorDetails: false,
|
288
|
-
errors: true,
|
289
|
-
errorsCount: true,
|
290
|
-
hash: true,
|
291
|
-
modules: true,
|
292
|
-
name: true,
|
293
|
-
outputPath: true,
|
294
|
-
timings: true,
|
295
|
-
warnings: true,
|
296
|
-
warningsCount: true,
|
297
|
-
},
|
298
|
-
}
|
299
|
-
|
300
|
-
export {Compiler as default}
|