@flareapp/webpack 2.0.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/.oxlintrc.json +7 -0
- package/README.md +52 -0
- package/dist/index.cjs +195 -0
- package/dist/index.d.cts +35 -0
- package/dist/index.d.mts +35 -0
- package/dist/index.mjs +166 -0
- package/package.json +58 -0
package/.oxlintrc.json
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# @flareapp/webpack
|
|
2
|
+
|
|
3
|
+
Webpack 5 plugin for [Flare](https://flareapp.io) that uploads sourcemaps after each build. With sourcemaps, error reports sent by `@flareapp/js` will show the original source code instead of minified output.
|
|
4
|
+
|
|
5
|
+
The plugin also injects the Flare API key and a sourcemap version identifier into your build via `DefinePlugin`, so `flare.light()` works without any additional configuration.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @flareapp/webpack
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
Add the plugin to your `webpack.config.js`:
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
const { FlareWebpackPlugin } = require('@flareapp/webpack');
|
|
19
|
+
|
|
20
|
+
module.exports = {
|
|
21
|
+
// ...
|
|
22
|
+
devtool: 'source-map',
|
|
23
|
+
plugins: [
|
|
24
|
+
new FlareWebpackPlugin({
|
|
25
|
+
apiKey: 'YOUR_FLARE_API_KEY',
|
|
26
|
+
}),
|
|
27
|
+
],
|
|
28
|
+
};
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Options
|
|
32
|
+
|
|
33
|
+
| Option | Type | Default | Description |
|
|
34
|
+
| ------------------ | --------- | ------------------------------------ | -------------------------------------------------- |
|
|
35
|
+
| `apiKey` | `string` | (required) | Your Flare API key |
|
|
36
|
+
| `apiEndpoint` | `string` | `https://flareapp.io/api/sourcemaps` | Sourcemap upload endpoint |
|
|
37
|
+
| `version` | `string` | random UUID | Sourcemap version identifier |
|
|
38
|
+
| `removeSourcemaps` | `boolean` | `false` | Delete `.map` files from build output after upload |
|
|
39
|
+
| `runInDevelopment` | `boolean` | `false` | Upload sourcemaps even in development mode |
|
|
40
|
+
| `publicPath` | `string` | from webpack config | Override the public path prepended to filenames |
|
|
41
|
+
|
|
42
|
+
## Compatibility
|
|
43
|
+
|
|
44
|
+
- Webpack 5
|
|
45
|
+
|
|
46
|
+
## Documentation
|
|
47
|
+
|
|
48
|
+
Full documentation is available at [flareapp.io/docs/javascript/general/resolving-bundled-code](https://flareapp.io/docs/javascript/general/resolving-bundled-code).
|
|
49
|
+
|
|
50
|
+
## License
|
|
51
|
+
|
|
52
|
+
The MIT License (MIT). Please see [License File](../../LICENSE.md) for more information.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
12
|
+
key = keys[i];
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
14
|
+
__defProp(to, key, {
|
|
15
|
+
get: ((k) => from[k]).bind(null, key),
|
|
16
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
|
+
};
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
24
|
+
value: mod,
|
|
25
|
+
enumerable: true
|
|
26
|
+
}) : target, mod));
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
let node_crypto = require("node:crypto");
|
|
30
|
+
let node_fs = require("node:fs");
|
|
31
|
+
let node_path = require("node:path");
|
|
32
|
+
let node_zlib = require("node:zlib");
|
|
33
|
+
let webpack = require("webpack");
|
|
34
|
+
webpack = __toESM(webpack);
|
|
35
|
+
|
|
36
|
+
//#region ../flare-api/dist/index.mjs
|
|
37
|
+
var FlareApiError = class extends Error {
|
|
38
|
+
constructor(message) {
|
|
39
|
+
super(message);
|
|
40
|
+
this.name = "FlareApiError";
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const RETRIABLE_STATUS_CODES = new Set([
|
|
44
|
+
429,
|
|
45
|
+
502,
|
|
46
|
+
503,
|
|
47
|
+
504
|
|
48
|
+
]);
|
|
49
|
+
var FlareApi = class {
|
|
50
|
+
constructor(endpoint, key, version) {
|
|
51
|
+
this.endpoint = endpoint;
|
|
52
|
+
this.key = key;
|
|
53
|
+
this.version = version;
|
|
54
|
+
}
|
|
55
|
+
uploadSourcemap(sourcemap) {
|
|
56
|
+
const base64GzipSourcemap = (0, node_zlib.deflateRawSync)(sourcemap.content).toString("base64");
|
|
57
|
+
return this.postWithRetry({
|
|
58
|
+
key: this.key,
|
|
59
|
+
version_id: this.version,
|
|
60
|
+
relative_filename: sourcemap.originalFile,
|
|
61
|
+
sourcemap: base64GzipSourcemap
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async postWithRetry(data, maxRetries = 3) {
|
|
65
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
66
|
+
try {
|
|
67
|
+
const response = await fetch(this.endpoint, {
|
|
68
|
+
method: "POST",
|
|
69
|
+
headers: { "Content-Type": "application/json" },
|
|
70
|
+
body: JSON.stringify(data)
|
|
71
|
+
});
|
|
72
|
+
if (response.ok) return;
|
|
73
|
+
if (!RETRIABLE_STATUS_CODES.has(response.status)) {
|
|
74
|
+
const body = await response.text();
|
|
75
|
+
throw new FlareApiError(`Flare API returned ${response.status}: ${body}`);
|
|
76
|
+
}
|
|
77
|
+
if (attempt === maxRetries) throw new FlareApiError(`Flare API returned ${response.status} after ${maxRetries} attempts`);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (error instanceof FlareApiError) throw error;
|
|
80
|
+
if (attempt === maxRetries) {
|
|
81
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
82
|
+
throw new Error(`Network error after ${maxRetries} attempts: ${message}`, { cause: error });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
await this.delay(Math.pow(2, attempt - 1) * 1e3);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
delay(ms) {
|
|
89
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region src/FlareWebpackPlugin.ts
|
|
95
|
+
function log(message, isError = false) {
|
|
96
|
+
const formatted = `@flareapp/webpack: ${message}`;
|
|
97
|
+
if (isError) console.error(formatted);
|
|
98
|
+
else console.log(formatted);
|
|
99
|
+
}
|
|
100
|
+
var FlareWebpackPlugin = class {
|
|
101
|
+
apiKey;
|
|
102
|
+
apiEndpoint;
|
|
103
|
+
runInDevelopment;
|
|
104
|
+
version;
|
|
105
|
+
removeSourcemaps;
|
|
106
|
+
publicPathOverride;
|
|
107
|
+
constructor({ apiKey, apiEndpoint = "https://flareapp.io/api/sourcemaps", runInDevelopment = false, version = (0, node_crypto.randomUUID)(), removeSourcemaps = false, publicPath }) {
|
|
108
|
+
this.apiKey = apiKey;
|
|
109
|
+
this.apiEndpoint = apiEndpoint;
|
|
110
|
+
this.runInDevelopment = runInDevelopment;
|
|
111
|
+
this.version = version;
|
|
112
|
+
this.removeSourcemaps = removeSourcemaps;
|
|
113
|
+
this.publicPathOverride = publicPath;
|
|
114
|
+
}
|
|
115
|
+
apply(compiler) {
|
|
116
|
+
const { DefinePlugin } = webpack.default;
|
|
117
|
+
new DefinePlugin({
|
|
118
|
+
FLARE_JS_KEY: JSON.stringify(this.apiKey),
|
|
119
|
+
FLARE_SOURCEMAP_VERSION: JSON.stringify(this.version)
|
|
120
|
+
}).apply(compiler);
|
|
121
|
+
const flare = new FlareApi(this.apiEndpoint, this.apiKey, this.version);
|
|
122
|
+
compiler.hooks.afterEmit.tapPromise("FlareWebpackPlugin", async (compilation) => {
|
|
123
|
+
if (!this.shouldUpload(compiler, compilation)) return;
|
|
124
|
+
const resolvedPublicPath = this.resolvePublicPath(compiler);
|
|
125
|
+
const sourcemaps = this.getSourcemaps(compilation, resolvedPublicPath);
|
|
126
|
+
if (!sourcemaps.length) {
|
|
127
|
+
compilation.warnings.push(new webpack.default.WebpackError("@flareapp/webpack: No sourcemap files found. Make sure sourcemaps are enabled in your webpack config."));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
log(`Uploading ${sourcemaps.length} sourcemap(s) to Flare.`);
|
|
131
|
+
const results = await Promise.allSettled(sourcemaps.map(({ sourcemap }) => flare.uploadSourcemap(sourcemap)));
|
|
132
|
+
const failed = results.filter((r) => r.status === "rejected");
|
|
133
|
+
if (failed.length > 0) for (const result of failed) compilation.warnings.push(new webpack.default.WebpackError(`@flareapp/webpack: Upload failed: ${result.reason}`));
|
|
134
|
+
else log("Successfully uploaded all sourcemaps to Flare.");
|
|
135
|
+
if (this.removeSourcemaps) {
|
|
136
|
+
for (let i = 0; i < sourcemaps.length; i++) {
|
|
137
|
+
if (results[i].status === "rejected") continue;
|
|
138
|
+
try {
|
|
139
|
+
(0, node_fs.unlinkSync)(sourcemaps[i].path);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
log(`Error removing ${sourcemaps[i].path}: ${error}`, true);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
log("Removed sourcemap files from build output.");
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
shouldUpload(compiler, compilation) {
|
|
149
|
+
if (!this.apiKey) {
|
|
150
|
+
compilation.warnings.push(new webpack.default.WebpackError("@flareapp/webpack: No Flare API key provided, not uploading sourcemaps."));
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
if (!this.runInDevelopment && compiler.options.mode === "development") {
|
|
154
|
+
log("Running webpack in development mode, not uploading sourcemaps.");
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
if (compiler.options.watch) {
|
|
158
|
+
log("Running webpack in watch mode, not uploading sourcemaps.");
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
resolvePublicPath(compiler) {
|
|
164
|
+
if (this.publicPathOverride != null) return this.publicPathOverride.endsWith("/") ? this.publicPathOverride : `${this.publicPathOverride}/`;
|
|
165
|
+
const configPublicPath = compiler.options.output?.publicPath;
|
|
166
|
+
if (typeof configPublicPath === "string" && configPublicPath && configPublicPath !== "auto") return configPublicPath.endsWith("/") ? configPublicPath : `${configPublicPath}/`;
|
|
167
|
+
return "/";
|
|
168
|
+
}
|
|
169
|
+
getSourcemaps(compilation, publicPath) {
|
|
170
|
+
const outputPath = compilation.getPath(compilation.compiler.outputPath);
|
|
171
|
+
const sourcemaps = [];
|
|
172
|
+
for (const chunk of compilation.chunks) {
|
|
173
|
+
const jsFile = [...chunk.files].find((file) => file.endsWith(".js"));
|
|
174
|
+
const mapFile = [...chunk.auxiliaryFiles].find((file) => file.endsWith(".js.map"));
|
|
175
|
+
if (!jsFile || !mapFile) continue;
|
|
176
|
+
const mapPath = (0, node_path.join)(outputPath, mapFile);
|
|
177
|
+
try {
|
|
178
|
+
const content = (0, node_fs.readFileSync)(mapPath, "utf8");
|
|
179
|
+
sourcemaps.push({
|
|
180
|
+
sourcemap: {
|
|
181
|
+
originalFile: `${publicPath}${jsFile}`,
|
|
182
|
+
content
|
|
183
|
+
},
|
|
184
|
+
path: mapPath
|
|
185
|
+
});
|
|
186
|
+
} catch (error) {
|
|
187
|
+
log(`Error reading sourcemap ${mapPath}: ${error}`, true);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return sourcemaps;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
//#endregion
|
|
195
|
+
exports.FlareWebpackPlugin = FlareWebpackPlugin;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Compiler } from "webpack";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
type FlareWebpackPluginOptions = {
|
|
5
|
+
apiKey: string;
|
|
6
|
+
apiEndpoint?: string;
|
|
7
|
+
runInDevelopment?: boolean;
|
|
8
|
+
version?: string;
|
|
9
|
+
removeSourcemaps?: boolean;
|
|
10
|
+
publicPath?: string;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/FlareWebpackPlugin.d.ts
|
|
14
|
+
declare class FlareWebpackPlugin {
|
|
15
|
+
private readonly apiKey;
|
|
16
|
+
private readonly apiEndpoint;
|
|
17
|
+
private readonly runInDevelopment;
|
|
18
|
+
private readonly version;
|
|
19
|
+
private readonly removeSourcemaps;
|
|
20
|
+
private readonly publicPathOverride;
|
|
21
|
+
constructor({
|
|
22
|
+
apiKey,
|
|
23
|
+
apiEndpoint,
|
|
24
|
+
runInDevelopment,
|
|
25
|
+
version,
|
|
26
|
+
removeSourcemaps,
|
|
27
|
+
publicPath
|
|
28
|
+
}: FlareWebpackPluginOptions);
|
|
29
|
+
apply(compiler: Compiler): void;
|
|
30
|
+
private shouldUpload;
|
|
31
|
+
private resolvePublicPath;
|
|
32
|
+
private getSourcemaps;
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { FlareWebpackPlugin, type FlareWebpackPluginOptions };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Compiler } from "webpack";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
type FlareWebpackPluginOptions = {
|
|
5
|
+
apiKey: string;
|
|
6
|
+
apiEndpoint?: string;
|
|
7
|
+
runInDevelopment?: boolean;
|
|
8
|
+
version?: string;
|
|
9
|
+
removeSourcemaps?: boolean;
|
|
10
|
+
publicPath?: string;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/FlareWebpackPlugin.d.ts
|
|
14
|
+
declare class FlareWebpackPlugin {
|
|
15
|
+
private readonly apiKey;
|
|
16
|
+
private readonly apiEndpoint;
|
|
17
|
+
private readonly runInDevelopment;
|
|
18
|
+
private readonly version;
|
|
19
|
+
private readonly removeSourcemaps;
|
|
20
|
+
private readonly publicPathOverride;
|
|
21
|
+
constructor({
|
|
22
|
+
apiKey,
|
|
23
|
+
apiEndpoint,
|
|
24
|
+
runInDevelopment,
|
|
25
|
+
version,
|
|
26
|
+
removeSourcemaps,
|
|
27
|
+
publicPath
|
|
28
|
+
}: FlareWebpackPluginOptions);
|
|
29
|
+
apply(compiler: Compiler): void;
|
|
30
|
+
private shouldUpload;
|
|
31
|
+
private resolvePublicPath;
|
|
32
|
+
private getSourcemaps;
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { FlareWebpackPlugin, type FlareWebpackPluginOptions };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { readFileSync, unlinkSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { deflateRawSync } from "node:zlib";
|
|
5
|
+
import webpack from "webpack";
|
|
6
|
+
|
|
7
|
+
//#region ../flare-api/dist/index.mjs
|
|
8
|
+
var FlareApiError = class extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "FlareApiError";
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
const RETRIABLE_STATUS_CODES = new Set([
|
|
15
|
+
429,
|
|
16
|
+
502,
|
|
17
|
+
503,
|
|
18
|
+
504
|
|
19
|
+
]);
|
|
20
|
+
var FlareApi = class {
|
|
21
|
+
constructor(endpoint, key, version) {
|
|
22
|
+
this.endpoint = endpoint;
|
|
23
|
+
this.key = key;
|
|
24
|
+
this.version = version;
|
|
25
|
+
}
|
|
26
|
+
uploadSourcemap(sourcemap) {
|
|
27
|
+
const base64GzipSourcemap = deflateRawSync(sourcemap.content).toString("base64");
|
|
28
|
+
return this.postWithRetry({
|
|
29
|
+
key: this.key,
|
|
30
|
+
version_id: this.version,
|
|
31
|
+
relative_filename: sourcemap.originalFile,
|
|
32
|
+
sourcemap: base64GzipSourcemap
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async postWithRetry(data, maxRetries = 3) {
|
|
36
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
37
|
+
try {
|
|
38
|
+
const response = await fetch(this.endpoint, {
|
|
39
|
+
method: "POST",
|
|
40
|
+
headers: { "Content-Type": "application/json" },
|
|
41
|
+
body: JSON.stringify(data)
|
|
42
|
+
});
|
|
43
|
+
if (response.ok) return;
|
|
44
|
+
if (!RETRIABLE_STATUS_CODES.has(response.status)) {
|
|
45
|
+
const body = await response.text();
|
|
46
|
+
throw new FlareApiError(`Flare API returned ${response.status}: ${body}`);
|
|
47
|
+
}
|
|
48
|
+
if (attempt === maxRetries) throw new FlareApiError(`Flare API returned ${response.status} after ${maxRetries} attempts`);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (error instanceof FlareApiError) throw error;
|
|
51
|
+
if (attempt === maxRetries) {
|
|
52
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
53
|
+
throw new Error(`Network error after ${maxRetries} attempts: ${message}`, { cause: error });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
await this.delay(Math.pow(2, attempt - 1) * 1e3);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
delay(ms) {
|
|
60
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/FlareWebpackPlugin.ts
|
|
66
|
+
function log(message, isError = false) {
|
|
67
|
+
const formatted = `@flareapp/webpack: ${message}`;
|
|
68
|
+
if (isError) console.error(formatted);
|
|
69
|
+
else console.log(formatted);
|
|
70
|
+
}
|
|
71
|
+
var FlareWebpackPlugin = class {
|
|
72
|
+
apiKey;
|
|
73
|
+
apiEndpoint;
|
|
74
|
+
runInDevelopment;
|
|
75
|
+
version;
|
|
76
|
+
removeSourcemaps;
|
|
77
|
+
publicPathOverride;
|
|
78
|
+
constructor({ apiKey, apiEndpoint = "https://flareapp.io/api/sourcemaps", runInDevelopment = false, version = randomUUID(), removeSourcemaps = false, publicPath }) {
|
|
79
|
+
this.apiKey = apiKey;
|
|
80
|
+
this.apiEndpoint = apiEndpoint;
|
|
81
|
+
this.runInDevelopment = runInDevelopment;
|
|
82
|
+
this.version = version;
|
|
83
|
+
this.removeSourcemaps = removeSourcemaps;
|
|
84
|
+
this.publicPathOverride = publicPath;
|
|
85
|
+
}
|
|
86
|
+
apply(compiler) {
|
|
87
|
+
const { DefinePlugin } = webpack;
|
|
88
|
+
new DefinePlugin({
|
|
89
|
+
FLARE_JS_KEY: JSON.stringify(this.apiKey),
|
|
90
|
+
FLARE_SOURCEMAP_VERSION: JSON.stringify(this.version)
|
|
91
|
+
}).apply(compiler);
|
|
92
|
+
const flare = new FlareApi(this.apiEndpoint, this.apiKey, this.version);
|
|
93
|
+
compiler.hooks.afterEmit.tapPromise("FlareWebpackPlugin", async (compilation) => {
|
|
94
|
+
if (!this.shouldUpload(compiler, compilation)) return;
|
|
95
|
+
const resolvedPublicPath = this.resolvePublicPath(compiler);
|
|
96
|
+
const sourcemaps = this.getSourcemaps(compilation, resolvedPublicPath);
|
|
97
|
+
if (!sourcemaps.length) {
|
|
98
|
+
compilation.warnings.push(new webpack.WebpackError("@flareapp/webpack: No sourcemap files found. Make sure sourcemaps are enabled in your webpack config."));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
log(`Uploading ${sourcemaps.length} sourcemap(s) to Flare.`);
|
|
102
|
+
const results = await Promise.allSettled(sourcemaps.map(({ sourcemap }) => flare.uploadSourcemap(sourcemap)));
|
|
103
|
+
const failed = results.filter((r) => r.status === "rejected");
|
|
104
|
+
if (failed.length > 0) for (const result of failed) compilation.warnings.push(new webpack.WebpackError(`@flareapp/webpack: Upload failed: ${result.reason}`));
|
|
105
|
+
else log("Successfully uploaded all sourcemaps to Flare.");
|
|
106
|
+
if (this.removeSourcemaps) {
|
|
107
|
+
for (let i = 0; i < sourcemaps.length; i++) {
|
|
108
|
+
if (results[i].status === "rejected") continue;
|
|
109
|
+
try {
|
|
110
|
+
unlinkSync(sourcemaps[i].path);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
log(`Error removing ${sourcemaps[i].path}: ${error}`, true);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
log("Removed sourcemap files from build output.");
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
shouldUpload(compiler, compilation) {
|
|
120
|
+
if (!this.apiKey) {
|
|
121
|
+
compilation.warnings.push(new webpack.WebpackError("@flareapp/webpack: No Flare API key provided, not uploading sourcemaps."));
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
if (!this.runInDevelopment && compiler.options.mode === "development") {
|
|
125
|
+
log("Running webpack in development mode, not uploading sourcemaps.");
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
if (compiler.options.watch) {
|
|
129
|
+
log("Running webpack in watch mode, not uploading sourcemaps.");
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
resolvePublicPath(compiler) {
|
|
135
|
+
if (this.publicPathOverride != null) return this.publicPathOverride.endsWith("/") ? this.publicPathOverride : `${this.publicPathOverride}/`;
|
|
136
|
+
const configPublicPath = compiler.options.output?.publicPath;
|
|
137
|
+
if (typeof configPublicPath === "string" && configPublicPath && configPublicPath !== "auto") return configPublicPath.endsWith("/") ? configPublicPath : `${configPublicPath}/`;
|
|
138
|
+
return "/";
|
|
139
|
+
}
|
|
140
|
+
getSourcemaps(compilation, publicPath) {
|
|
141
|
+
const outputPath = compilation.getPath(compilation.compiler.outputPath);
|
|
142
|
+
const sourcemaps = [];
|
|
143
|
+
for (const chunk of compilation.chunks) {
|
|
144
|
+
const jsFile = [...chunk.files].find((file) => file.endsWith(".js"));
|
|
145
|
+
const mapFile = [...chunk.auxiliaryFiles].find((file) => file.endsWith(".js.map"));
|
|
146
|
+
if (!jsFile || !mapFile) continue;
|
|
147
|
+
const mapPath = join(outputPath, mapFile);
|
|
148
|
+
try {
|
|
149
|
+
const content = readFileSync(mapPath, "utf8");
|
|
150
|
+
sourcemaps.push({
|
|
151
|
+
sourcemap: {
|
|
152
|
+
originalFile: `${publicPath}${jsFile}`,
|
|
153
|
+
content
|
|
154
|
+
},
|
|
155
|
+
path: mapPath
|
|
156
|
+
});
|
|
157
|
+
} catch (error) {
|
|
158
|
+
log(`Error reading sourcemap ${mapPath}: ${error}`, true);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return sourcemaps;
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
//#endregion
|
|
166
|
+
export { FlareWebpackPlugin };
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flareapp/webpack",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Webpack plugin for uploading sourcemaps to Flare",
|
|
5
|
+
"homepage": "https://flareapp.io",
|
|
6
|
+
"bugs": "https://github.com/spatie/flare-client-js/issues",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/spatie/flare-client-js.git"
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"author": {
|
|
13
|
+
"name": "Spatie",
|
|
14
|
+
"email": "info@spatie.be"
|
|
15
|
+
},
|
|
16
|
+
"contributors": [
|
|
17
|
+
"Dries Heyninck <dries@spatie.be>"
|
|
18
|
+
],
|
|
19
|
+
"main": "./dist/index.cjs",
|
|
20
|
+
"module": "./dist/index.mjs",
|
|
21
|
+
"types": "./dist/index.d.cts",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"import": {
|
|
25
|
+
"types": "./dist/index.d.mts",
|
|
26
|
+
"default": "./dist/index.mjs"
|
|
27
|
+
},
|
|
28
|
+
"require": {
|
|
29
|
+
"types": "./dist/index.d.cts",
|
|
30
|
+
"default": "./dist/index.cjs"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"prepublishOnly": "npm run build",
|
|
39
|
+
"build": "tsdown src/index.ts --format cjs,esm --dts --clean --noExternal @flareapp/flare-api",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"typescript": "tsc --noEmit",
|
|
42
|
+
"release": "release-it"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@flareapp/flare-api": "*",
|
|
46
|
+
"@types/webpack": "^5.0.0",
|
|
47
|
+
"tsdown": "^0.20.3",
|
|
48
|
+
"typescript": "^5.7.0",
|
|
49
|
+
"vitest": "^4.0.0",
|
|
50
|
+
"webpack": "^5.0.0"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"webpack": "^5.0.0"
|
|
54
|
+
},
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public"
|
|
57
|
+
}
|
|
58
|
+
}
|