@flareapp/react-native-sourcemaps 2.6.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/README.md +257 -0
- package/dist/babel.cjs +52 -0
- package/dist/babel.d.cts +18 -0
- package/dist/babel.d.mts +19 -0
- package/dist/babel.mjs +52 -0
- package/dist/bin.cjs +251 -0
- package/dist/bin.d.cts +1 -0
- package/dist/bin.d.mts +1 -0
- package/dist/bin.mjs +252 -0
- package/dist/expo.cjs +254 -0
- package/dist/expo.d.cts +11 -0
- package/dist/index-legacy-DeTFfC6B.d.mts +2557 -0
- package/dist/index-legacy-aO8l3fYt.d.cts +2557 -0
- package/dist/index.cjs +8 -0
- package/dist/index.d.cts +33 -0
- package/dist/index.d.mts +33 -0
- package/dist/index.mjs +5 -0
- package/dist/runtime.cjs +18 -0
- package/dist/runtime.d.cts +15 -0
- package/dist/runtime.d.mts +15 -0
- package/dist/runtime.mjs +16 -0
- package/dist/uploadSourcemaps-C2EGBxe1.mjs +91 -0
- package/dist/uploadSourcemaps-D6J5FWDO.cjs +96 -0
- package/dist/version-DnFqflPl.mjs +49 -0
- package/dist/version-Z9pawNvw.cjs +66 -0
- package/flare.gradle +103 -0
- package/package.json +100 -0
- package/scripts/flare-xcode.sh +77 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_uploadSourcemaps = require('./uploadSourcemaps-D6J5FWDO.cjs');
|
|
3
|
+
const require_version = require('./version-Z9pawNvw.cjs');
|
|
4
|
+
const require_babel = require('./babel.cjs');
|
|
5
|
+
|
|
6
|
+
exports.flareSourcemapsBabelPlugin = require_babel;
|
|
7
|
+
exports.resolveVersion = require_version.resolveVersion;
|
|
8
|
+
exports.uploadSourcemaps = require_uploadSourcemaps.uploadSourcemaps;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import flareSourcemapsBabelPlugin from "./babel.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/uploadSourcemaps.d.ts
|
|
4
|
+
type UploadSourcemapsOptions = {
|
|
5
|
+
/** Flare project API key. */apiKey: string; /** Path to the composed `.map` file to upload. */
|
|
6
|
+
sourcemap: string;
|
|
7
|
+
/**
|
|
8
|
+
* The `relative_filename` the backend matches against runtime stack frames.
|
|
9
|
+
* Defaults to the map's basename minus `.map` (e.g. `index.android.bundle`).
|
|
10
|
+
*/
|
|
11
|
+
bundleFilename?: string; /** Explicit version; otherwise resolved from env/package.json. */
|
|
12
|
+
version?: string; /** Override the Flare sourcemaps endpoint. */
|
|
13
|
+
apiEndpoint?: string;
|
|
14
|
+
};
|
|
15
|
+
declare function uploadSourcemaps(options: UploadSourcemapsOptions): Promise<void>;
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/version.d.ts
|
|
18
|
+
type ResolveVersionOptions = {
|
|
19
|
+
/** Explicit version (e.g. CLI `--version`). Highest precedence. */version?: string; /** Directory whose package.json is read as the last-resort fallback. */
|
|
20
|
+
cwd?: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the sourcemap version shared by the Babel plugin and the CLI.
|
|
24
|
+
* Precedence: explicit `version` > `FLARE_SOURCEMAP_VERSION` env > package.json
|
|
25
|
+
* `version` (with a warning). Never a random value — a random default would
|
|
26
|
+
* silently desync the inlined runtime value from the uploaded `version_id`.
|
|
27
|
+
*/
|
|
28
|
+
declare function resolveVersion({
|
|
29
|
+
version,
|
|
30
|
+
cwd
|
|
31
|
+
}?: ResolveVersionOptions): string;
|
|
32
|
+
//#endregion
|
|
33
|
+
export { type ResolveVersionOptions, type UploadSourcemapsOptions, flareSourcemapsBabelPlugin, resolveVersion, uploadSourcemaps };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import flareSourcemapsBabelPlugin from "./babel.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/uploadSourcemaps.d.ts
|
|
4
|
+
type UploadSourcemapsOptions = {
|
|
5
|
+
/** Flare project API key. */apiKey: string; /** Path to the composed `.map` file to upload. */
|
|
6
|
+
sourcemap: string;
|
|
7
|
+
/**
|
|
8
|
+
* The `relative_filename` the backend matches against runtime stack frames.
|
|
9
|
+
* Defaults to the map's basename minus `.map` (e.g. `index.android.bundle`).
|
|
10
|
+
*/
|
|
11
|
+
bundleFilename?: string; /** Explicit version; otherwise resolved from env/package.json. */
|
|
12
|
+
version?: string; /** Override the Flare sourcemaps endpoint. */
|
|
13
|
+
apiEndpoint?: string;
|
|
14
|
+
};
|
|
15
|
+
declare function uploadSourcemaps(options: UploadSourcemapsOptions): Promise<void>;
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/version.d.ts
|
|
18
|
+
type ResolveVersionOptions = {
|
|
19
|
+
/** Explicit version (e.g. CLI `--version`). Highest precedence. */version?: string; /** Directory whose package.json is read as the last-resort fallback. */
|
|
20
|
+
cwd?: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the sourcemap version shared by the Babel plugin and the CLI.
|
|
24
|
+
* Precedence: explicit `version` > `FLARE_SOURCEMAP_VERSION` env > package.json
|
|
25
|
+
* `version` (with a warning). Never a random value — a random default would
|
|
26
|
+
* silently desync the inlined runtime value from the uploaded `version_id`.
|
|
27
|
+
*/
|
|
28
|
+
declare function resolveVersion({
|
|
29
|
+
version,
|
|
30
|
+
cwd
|
|
31
|
+
}?: ResolveVersionOptions): string;
|
|
32
|
+
//#endregion
|
|
33
|
+
export { type ResolveVersionOptions, type UploadSourcemapsOptions, flareSourcemapsBabelPlugin, resolveVersion, uploadSourcemaps };
|
package/dist/index.mjs
ADDED
package/dist/runtime.cjs
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
//#region src/runtime.ts
|
|
4
|
+
/**
|
|
5
|
+
* The build-time sourcemap version, for `flare.configure({ sourcemapVersionId })`.
|
|
6
|
+
*
|
|
7
|
+
* `@flareapp/react-native-sourcemaps/babel` replaces every reference to this
|
|
8
|
+
* binding with the resolved version string literal at bundle time, then drops the
|
|
9
|
+
* import. Without that Babel plugin it stays an empty string (meaning "no version"),
|
|
10
|
+
* which is harmless because sourcemaps are only uploaded for release builds.
|
|
11
|
+
*
|
|
12
|
+
* This module is runtime-safe: it has no Node imports, so it is safe to import into
|
|
13
|
+
* app code that Metro bundles (unlike the package root, which pulls in the uploader).
|
|
14
|
+
*/
|
|
15
|
+
const flareSourcemapVersion = "";
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
exports.flareSourcemapVersion = flareSourcemapVersion;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/runtime.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The build-time sourcemap version, for `flare.configure({ sourcemapVersionId })`.
|
|
4
|
+
*
|
|
5
|
+
* `@flareapp/react-native-sourcemaps/babel` replaces every reference to this
|
|
6
|
+
* binding with the resolved version string literal at bundle time, then drops the
|
|
7
|
+
* import. Without that Babel plugin it stays an empty string (meaning "no version"),
|
|
8
|
+
* which is harmless because sourcemaps are only uploaded for release builds.
|
|
9
|
+
*
|
|
10
|
+
* This module is runtime-safe: it has no Node imports, so it is safe to import into
|
|
11
|
+
* app code that Metro bundles (unlike the package root, which pulls in the uploader).
|
|
12
|
+
*/
|
|
13
|
+
declare const flareSourcemapVersion: string;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { flareSourcemapVersion };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/runtime.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The build-time sourcemap version, for `flare.configure({ sourcemapVersionId })`.
|
|
4
|
+
*
|
|
5
|
+
* `@flareapp/react-native-sourcemaps/babel` replaces every reference to this
|
|
6
|
+
* binding with the resolved version string literal at bundle time, then drops the
|
|
7
|
+
* import. Without that Babel plugin it stays an empty string (meaning "no version"),
|
|
8
|
+
* which is harmless because sourcemaps are only uploaded for release builds.
|
|
9
|
+
*
|
|
10
|
+
* This module is runtime-safe: it has no Node imports, so it is safe to import into
|
|
11
|
+
* app code that Metro bundles (unlike the package root, which pulls in the uploader).
|
|
12
|
+
*/
|
|
13
|
+
declare const flareSourcemapVersion: string;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { flareSourcemapVersion };
|
package/dist/runtime.mjs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/runtime.ts
|
|
2
|
+
/**
|
|
3
|
+
* The build-time sourcemap version, for `flare.configure({ sourcemapVersionId })`.
|
|
4
|
+
*
|
|
5
|
+
* `@flareapp/react-native-sourcemaps/babel` replaces every reference to this
|
|
6
|
+
* binding with the resolved version string literal at bundle time, then drops the
|
|
7
|
+
* import. Without that Babel plugin it stays an empty string (meaning "no version"),
|
|
8
|
+
* which is harmless because sourcemaps are only uploaded for release builds.
|
|
9
|
+
*
|
|
10
|
+
* This module is runtime-safe: it has no Node imports, so it is safe to import into
|
|
11
|
+
* app code that Metro bundles (unlike the package root, which pulls in the uploader).
|
|
12
|
+
*/
|
|
13
|
+
const flareSourcemapVersion = "";
|
|
14
|
+
|
|
15
|
+
//#endregion
|
|
16
|
+
export { flareSourcemapVersion };
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { n as resolveVersion, r as LOG_PREFIX } from "./version-DnFqflPl.mjs";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { basename } from "node:path";
|
|
4
|
+
import { deflateRawSync } from "node:zlib";
|
|
5
|
+
|
|
6
|
+
//#region ../flare-api/dist/index.mjs
|
|
7
|
+
var FlareApiError = class extends Error {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "FlareApiError";
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const RETRIABLE_STATUS_CODES = new Set([
|
|
14
|
+
429,
|
|
15
|
+
502,
|
|
16
|
+
503,
|
|
17
|
+
504
|
|
18
|
+
]);
|
|
19
|
+
var FlareApi = class {
|
|
20
|
+
constructor(endpoint, key, version) {
|
|
21
|
+
this.endpoint = endpoint;
|
|
22
|
+
this.key = key;
|
|
23
|
+
this.version = version;
|
|
24
|
+
}
|
|
25
|
+
uploadSourcemap(sourcemap) {
|
|
26
|
+
const base64GzipSourcemap = deflateRawSync(sourcemap.content).toString("base64");
|
|
27
|
+
return this.postWithRetry({
|
|
28
|
+
key: this.key,
|
|
29
|
+
version_id: this.version,
|
|
30
|
+
relative_filename: sourcemap.originalFile,
|
|
31
|
+
sourcemap: base64GzipSourcemap
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
async postWithRetry(data, maxRetries = 3) {
|
|
35
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
36
|
+
try {
|
|
37
|
+
const response = await fetch(this.endpoint, {
|
|
38
|
+
method: "POST",
|
|
39
|
+
headers: { "Content-Type": "application/json" },
|
|
40
|
+
body: JSON.stringify(data)
|
|
41
|
+
});
|
|
42
|
+
if (response.ok) return;
|
|
43
|
+
if (!RETRIABLE_STATUS_CODES.has(response.status)) {
|
|
44
|
+
const body = await response.text();
|
|
45
|
+
throw new FlareApiError(`Flare API returned ${response.status}: ${body}`);
|
|
46
|
+
}
|
|
47
|
+
if (attempt === maxRetries) throw new FlareApiError(`Flare API returned ${response.status} after ${maxRetries} attempts`);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (error instanceof FlareApiError) throw error;
|
|
50
|
+
if (attempt === maxRetries) {
|
|
51
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
52
|
+
throw new Error(`Network error after ${maxRetries} attempts: ${message}`, { cause: error });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
await this.delay(Math.pow(2, attempt - 1) * 1e3);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
delay(ms) {
|
|
59
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region src/uploadSourcemaps.ts
|
|
65
|
+
const DEFAULT_ENDPOINT = "https://flareapp.io/api/sourcemaps";
|
|
66
|
+
async function uploadSourcemaps(options) {
|
|
67
|
+
const { apiKey, sourcemap, apiEndpoint = DEFAULT_ENDPOINT } = options;
|
|
68
|
+
if (!apiKey) {
|
|
69
|
+
console.warn(`${LOG_PREFIX}: No Flare API key provided, not uploading sourcemaps.`);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const version = resolveVersion({ version: options.version });
|
|
73
|
+
const bundleFilename = options.bundleFilename ?? defaultBundleFilename(sourcemap);
|
|
74
|
+
const content = readFileSync(sourcemap, "utf8");
|
|
75
|
+
const flare = new FlareApi(apiEndpoint, apiKey, version);
|
|
76
|
+
log(`Uploading sourcemap "${bundleFilename}" (version ${version}) to Flare.`);
|
|
77
|
+
await flare.uploadSourcemap({
|
|
78
|
+
originalFile: bundleFilename,
|
|
79
|
+
content
|
|
80
|
+
});
|
|
81
|
+
log("Successfully uploaded sourcemap to Flare.");
|
|
82
|
+
}
|
|
83
|
+
function defaultBundleFilename(sourcemapPath) {
|
|
84
|
+
return basename(sourcemapPath).replace(/\.map$/, "");
|
|
85
|
+
}
|
|
86
|
+
function log(message) {
|
|
87
|
+
console.log(`${LOG_PREFIX}: ${message}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
//#endregion
|
|
91
|
+
export { uploadSourcemaps as t };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
const require_version = require('./version-Z9pawNvw.cjs');
|
|
2
|
+
let node_fs = require("node:fs");
|
|
3
|
+
let node_path = require("node:path");
|
|
4
|
+
let node_zlib = require("node:zlib");
|
|
5
|
+
|
|
6
|
+
//#region ../flare-api/dist/index.mjs
|
|
7
|
+
var FlareApiError = class extends Error {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "FlareApiError";
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const RETRIABLE_STATUS_CODES = new Set([
|
|
14
|
+
429,
|
|
15
|
+
502,
|
|
16
|
+
503,
|
|
17
|
+
504
|
|
18
|
+
]);
|
|
19
|
+
var FlareApi = class {
|
|
20
|
+
constructor(endpoint, key, version) {
|
|
21
|
+
this.endpoint = endpoint;
|
|
22
|
+
this.key = key;
|
|
23
|
+
this.version = version;
|
|
24
|
+
}
|
|
25
|
+
uploadSourcemap(sourcemap) {
|
|
26
|
+
const base64GzipSourcemap = (0, node_zlib.deflateRawSync)(sourcemap.content).toString("base64");
|
|
27
|
+
return this.postWithRetry({
|
|
28
|
+
key: this.key,
|
|
29
|
+
version_id: this.version,
|
|
30
|
+
relative_filename: sourcemap.originalFile,
|
|
31
|
+
sourcemap: base64GzipSourcemap
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
async postWithRetry(data, maxRetries = 3) {
|
|
35
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
36
|
+
try {
|
|
37
|
+
const response = await fetch(this.endpoint, {
|
|
38
|
+
method: "POST",
|
|
39
|
+
headers: { "Content-Type": "application/json" },
|
|
40
|
+
body: JSON.stringify(data)
|
|
41
|
+
});
|
|
42
|
+
if (response.ok) return;
|
|
43
|
+
if (!RETRIABLE_STATUS_CODES.has(response.status)) {
|
|
44
|
+
const body = await response.text();
|
|
45
|
+
throw new FlareApiError(`Flare API returned ${response.status}: ${body}`);
|
|
46
|
+
}
|
|
47
|
+
if (attempt === maxRetries) throw new FlareApiError(`Flare API returned ${response.status} after ${maxRetries} attempts`);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (error instanceof FlareApiError) throw error;
|
|
50
|
+
if (attempt === maxRetries) {
|
|
51
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
52
|
+
throw new Error(`Network error after ${maxRetries} attempts: ${message}`, { cause: error });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
await this.delay(Math.pow(2, attempt - 1) * 1e3);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
delay(ms) {
|
|
59
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region src/uploadSourcemaps.ts
|
|
65
|
+
const DEFAULT_ENDPOINT = "https://flareapp.io/api/sourcemaps";
|
|
66
|
+
async function uploadSourcemaps(options) {
|
|
67
|
+
const { apiKey, sourcemap, apiEndpoint = DEFAULT_ENDPOINT } = options;
|
|
68
|
+
if (!apiKey) {
|
|
69
|
+
console.warn(`${require_version.LOG_PREFIX}: No Flare API key provided, not uploading sourcemaps.`);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const version = require_version.resolveVersion({ version: options.version });
|
|
73
|
+
const bundleFilename = options.bundleFilename ?? defaultBundleFilename(sourcemap);
|
|
74
|
+
const content = (0, node_fs.readFileSync)(sourcemap, "utf8");
|
|
75
|
+
const flare = new FlareApi(apiEndpoint, apiKey, version);
|
|
76
|
+
log(`Uploading sourcemap "${bundleFilename}" (version ${version}) to Flare.`);
|
|
77
|
+
await flare.uploadSourcemap({
|
|
78
|
+
originalFile: bundleFilename,
|
|
79
|
+
content
|
|
80
|
+
});
|
|
81
|
+
log("Successfully uploaded sourcemap to Flare.");
|
|
82
|
+
}
|
|
83
|
+
function defaultBundleFilename(sourcemapPath) {
|
|
84
|
+
return (0, node_path.basename)(sourcemapPath).replace(/\.map$/, "");
|
|
85
|
+
}
|
|
86
|
+
function log(message) {
|
|
87
|
+
console.log(`${require_version.LOG_PREFIX}: ${message}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
//#endregion
|
|
91
|
+
Object.defineProperty(exports, 'uploadSourcemaps', {
|
|
92
|
+
enumerable: true,
|
|
93
|
+
get: function () {
|
|
94
|
+
return uploadSourcemaps;
|
|
95
|
+
}
|
|
96
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
|
|
4
|
+
//#region src/constants.ts
|
|
5
|
+
const LOG_PREFIX = "@flareapp/react-native-sourcemaps";
|
|
6
|
+
|
|
7
|
+
//#endregion
|
|
8
|
+
//#region src/version.ts
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the sourcemap version shared by the Babel plugin and the CLI.
|
|
11
|
+
* Precedence: explicit `version` > `FLARE_SOURCEMAP_VERSION` env > package.json
|
|
12
|
+
* `version` (with a warning). Never a random value — a random default would
|
|
13
|
+
* silently desync the inlined runtime value from the uploaded `version_id`.
|
|
14
|
+
*/
|
|
15
|
+
function resolveVersion({ version, cwd = process.cwd() } = {}) {
|
|
16
|
+
if (version) return version;
|
|
17
|
+
const envVersion = process.env.FLARE_SOURCEMAP_VERSION;
|
|
18
|
+
if (envVersion) return envVersion;
|
|
19
|
+
const pkgVersion = readPackageVersion(cwd);
|
|
20
|
+
if (pkgVersion) {
|
|
21
|
+
console.warn(`${LOG_PREFIX}: No version provided (--version or FLARE_SOURCEMAP_VERSION). Falling back to package.json version "${pkgVersion}". Use the same version when building the bundle and uploading the map, or symbolication will silently fail.`);
|
|
22
|
+
return pkgVersion;
|
|
23
|
+
}
|
|
24
|
+
throw new Error(`${LOG_PREFIX}: Could not resolve a sourcemap version. Pass --version, set FLARE_SOURCEMAP_VERSION, or ensure package.json has a "version".`);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Version resolution for the AUTOMATIC native upload path. Unlike resolveVersion it
|
|
28
|
+
* NEVER falls back to package.json: the hook runs in android/ or ios/, a different
|
|
29
|
+
* cwd than Metro, so a package.json fallback would read a different (or missing)
|
|
30
|
+
* file and silently desync from the version the Babel plugin inlined. The only
|
|
31
|
+
* input guaranteed identical to both halves is FLARE_SOURCEMAP_VERSION. Returns
|
|
32
|
+
* null when unresolved so the caller can skip-with-banner rather than upload a
|
|
33
|
+
* guaranteed-mismatched map.
|
|
34
|
+
*/
|
|
35
|
+
function resolveAutoVersion(version) {
|
|
36
|
+
if (version) return version;
|
|
37
|
+
const envVersion = process.env.FLARE_SOURCEMAP_VERSION;
|
|
38
|
+
return envVersion ? envVersion : null;
|
|
39
|
+
}
|
|
40
|
+
function readPackageVersion(cwd) {
|
|
41
|
+
try {
|
|
42
|
+
return JSON.parse(readFileSync(resolve(cwd, "package.json"), "utf8")).version ?? null;
|
|
43
|
+
} catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
//#endregion
|
|
49
|
+
export { resolveVersion as n, LOG_PREFIX as r, resolveAutoVersion as t };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
let node_fs = require("node:fs");
|
|
2
|
+
let node_path = require("node:path");
|
|
3
|
+
|
|
4
|
+
//#region src/constants.ts
|
|
5
|
+
const LOG_PREFIX = "@flareapp/react-native-sourcemaps";
|
|
6
|
+
|
|
7
|
+
//#endregion
|
|
8
|
+
//#region src/version.ts
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the sourcemap version shared by the Babel plugin and the CLI.
|
|
11
|
+
* Precedence: explicit `version` > `FLARE_SOURCEMAP_VERSION` env > package.json
|
|
12
|
+
* `version` (with a warning). Never a random value — a random default would
|
|
13
|
+
* silently desync the inlined runtime value from the uploaded `version_id`.
|
|
14
|
+
*/
|
|
15
|
+
function resolveVersion({ version, cwd = process.cwd() } = {}) {
|
|
16
|
+
if (version) return version;
|
|
17
|
+
const envVersion = process.env.FLARE_SOURCEMAP_VERSION;
|
|
18
|
+
if (envVersion) return envVersion;
|
|
19
|
+
const pkgVersion = readPackageVersion(cwd);
|
|
20
|
+
if (pkgVersion) {
|
|
21
|
+
console.warn(`${LOG_PREFIX}: No version provided (--version or FLARE_SOURCEMAP_VERSION). Falling back to package.json version "${pkgVersion}". Use the same version when building the bundle and uploading the map, or symbolication will silently fail.`);
|
|
22
|
+
return pkgVersion;
|
|
23
|
+
}
|
|
24
|
+
throw new Error(`${LOG_PREFIX}: Could not resolve a sourcemap version. Pass --version, set FLARE_SOURCEMAP_VERSION, or ensure package.json has a "version".`);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Version resolution for the AUTOMATIC native upload path. Unlike resolveVersion it
|
|
28
|
+
* NEVER falls back to package.json: the hook runs in android/ or ios/, a different
|
|
29
|
+
* cwd than Metro, so a package.json fallback would read a different (or missing)
|
|
30
|
+
* file and silently desync from the version the Babel plugin inlined. The only
|
|
31
|
+
* input guaranteed identical to both halves is FLARE_SOURCEMAP_VERSION. Returns
|
|
32
|
+
* null when unresolved so the caller can skip-with-banner rather than upload a
|
|
33
|
+
* guaranteed-mismatched map.
|
|
34
|
+
*/
|
|
35
|
+
function resolveAutoVersion(version) {
|
|
36
|
+
if (version) return version;
|
|
37
|
+
const envVersion = process.env.FLARE_SOURCEMAP_VERSION;
|
|
38
|
+
return envVersion ? envVersion : null;
|
|
39
|
+
}
|
|
40
|
+
function readPackageVersion(cwd) {
|
|
41
|
+
try {
|
|
42
|
+
return JSON.parse((0, node_fs.readFileSync)((0, node_path.resolve)(cwd, "package.json"), "utf8")).version ?? null;
|
|
43
|
+
} catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
//#endregion
|
|
49
|
+
Object.defineProperty(exports, 'LOG_PREFIX', {
|
|
50
|
+
enumerable: true,
|
|
51
|
+
get: function () {
|
|
52
|
+
return LOG_PREFIX;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
Object.defineProperty(exports, 'resolveAutoVersion', {
|
|
56
|
+
enumerable: true,
|
|
57
|
+
get: function () {
|
|
58
|
+
return resolveAutoVersion;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
Object.defineProperty(exports, 'resolveVersion', {
|
|
62
|
+
enumerable: true,
|
|
63
|
+
get: function () {
|
|
64
|
+
return resolveVersion;
|
|
65
|
+
}
|
|
66
|
+
});
|
package/flare.gradle
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// Flare automatic sourcemap upload for bare React Native (Android).
|
|
2
|
+
//
|
|
3
|
+
// Apply from android/app/build.gradle:
|
|
4
|
+
// apply from: "../../node_modules/@flareapp/react-native-sourcemaps/flare.gradle"
|
|
5
|
+
//
|
|
6
|
+
// Hooks the release JS-bundle task and, after it runs, uploads the Hermes-composed
|
|
7
|
+
// sourcemap to Flare. Never fails the build: the CLI exits 0 in --auto mode and the
|
|
8
|
+
// exec ignores its exit value as a belt-and-braces guard.
|
|
9
|
+
|
|
10
|
+
def flareProjectRoot = rootProject.projectDir.parentFile // android/ -> project root
|
|
11
|
+
def flareConfig = new File(flareProjectRoot, "flare.json")
|
|
12
|
+
// Prefer the resolved bin over `npx` (avoids npx's PATH ambiguity and any network
|
|
13
|
+
// fetch), matching the iOS hook. The no-extension unix bin is checked here; on
|
|
14
|
+
// Windows it is a `.cmd` this misses, so we fall back to `npx` there.
|
|
15
|
+
def flareCli = new File(flareProjectRoot, "node_modules/.bin/flare-rn-sourcemaps")
|
|
16
|
+
|
|
17
|
+
gradle.projectsEvaluated {
|
|
18
|
+
tasks.configureEach { task ->
|
|
19
|
+
def name = task.name
|
|
20
|
+
def isBundleTask = (name.startsWith("createBundle") || name.startsWith("bundle")) &&
|
|
21
|
+
name.endsWith("JsAndAssets")
|
|
22
|
+
// Don't require the variant name to contain "release". Brownfield/bare apps
|
|
23
|
+
// rename the release build type (Production, AppStore, ...), so requiring
|
|
24
|
+
// "release" would silently skip the upload on exactly the builds that ship.
|
|
25
|
+
// Skip only the dev (debug) variants, matched case-insensitively as a
|
|
26
|
+
// substring (symmetric with the iOS hook); the "no sourcemap -> skip" guard
|
|
27
|
+
// below then handles any non-bundling variant for free.
|
|
28
|
+
def isDebug = name.toLowerCase().contains("debug")
|
|
29
|
+
if (!isBundleTask || isDebug) {
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
task.doLast {
|
|
34
|
+
// Variant subdir (e.g. "release", "stagingRelease"), derived from the task
|
|
35
|
+
// name rather than hardcoded "release" so a renamed build type (Production,
|
|
36
|
+
// ...) still resolves. createBundle<Variant>JsAndAssets /
|
|
37
|
+
// bundle<Variant>JsAndAssets -> <variant>, decapitalised to match Gradle's
|
|
38
|
+
// camelCase variant name. `project` is the module this file is applied from;
|
|
39
|
+
// the documented usage is android/app/build.gradle, so project.projectDir is
|
|
40
|
+
// android/app.
|
|
41
|
+
def raw = name.replaceFirst(/^(createBundle|bundle)/, "").replaceFirst(/JsAndAssets$/, "")
|
|
42
|
+
def variant = raw.isEmpty() ? raw : raw.substring(0, 1).toLowerCase() + raw.substring(1)
|
|
43
|
+
|
|
44
|
+
// With Hermes, the map that resolves against the shipped bytecode is the
|
|
45
|
+
// COMPOSED map at build/generated/sourcemaps/react/<variant>/index.android.bundle.map.
|
|
46
|
+
// The packager map exposed via task.sourcemapOutput is the pre-Hermes metro
|
|
47
|
+
// map and would NOT line up, so prefer the composed map whenever it exists.
|
|
48
|
+
// Only fall back to sourcemapOutput for a non-Hermes (JSC) build, where the
|
|
49
|
+
// packager map IS the final map.
|
|
50
|
+
def composed = new File(
|
|
51
|
+
project.projectDir,
|
|
52
|
+
"build/generated/sourcemaps/react/${variant}/index.android.bundle.map"
|
|
53
|
+
)
|
|
54
|
+
def sourcemapPath
|
|
55
|
+
if (composed.exists()) {
|
|
56
|
+
sourcemapPath = composed.absolutePath
|
|
57
|
+
} else if (task.hasProperty("sourcemapOutput") && task.sourcemapOutput != null) {
|
|
58
|
+
sourcemapPath = task.sourcemapOutput.toString()
|
|
59
|
+
} else {
|
|
60
|
+
sourcemapPath = composed.absolutePath
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!new File(sourcemapPath).exists()) {
|
|
64
|
+
logger.warn("@flareapp/react-native-sourcemaps: no sourcemap at ${sourcemapPath}, skipping upload.")
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Prefer the resolved .bin; fall back to npx (Windows, or a missing bin).
|
|
69
|
+
def baseCommand = flareCli.exists() ? [flareCli.absolutePath] : ["npx", "flare-rn-sourcemaps"]
|
|
70
|
+
def command = baseCommand + [
|
|
71
|
+
"upload",
|
|
72
|
+
"--sourcemap", sourcemapPath,
|
|
73
|
+
"--config", flareConfig.absolutePath,
|
|
74
|
+
"--auto",
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
// Run via ProcessBuilder, NOT Gradle's `exec {}`. Inside a task action the
|
|
78
|
+
// closure delegate is the task, so a bare `exec {}` fails to resolve ("Could
|
|
79
|
+
// not find method exec()"), and `project.exec` is flagged by the
|
|
80
|
+
// configuration cache. ProcessBuilder has neither problem and inherits the
|
|
81
|
+
// build environment (FLARE_SOURCEMAP_VERSION / FLARE_API_KEY flow through; the
|
|
82
|
+
// CLI also auto-loads them from .env.local at the project root). The CLI exits
|
|
83
|
+
// 0 on every upload failure mode, so this can never break the build; the catch
|
|
84
|
+
// guards only a failure to launch the process (e.g. node not on PATH).
|
|
85
|
+
try {
|
|
86
|
+
def process = new ProcessBuilder(command)
|
|
87
|
+
.directory(flareProjectRoot)
|
|
88
|
+
.redirectErrorStream(true)
|
|
89
|
+
.start()
|
|
90
|
+
process.inputStream.eachLine { line -> logger.lifecycle(line) }
|
|
91
|
+
def exitCode = process.waitFor()
|
|
92
|
+
// The CLI always exits 0 in --auto mode (failures print the banner and
|
|
93
|
+
// return). A non-zero here means that contract regressed; surface it as
|
|
94
|
+
// a warning rather than swallowing it silently. Still never fails the build.
|
|
95
|
+
if (exitCode != 0) {
|
|
96
|
+
logger.warn("@flareapp/react-native-sourcemaps: upload CLI exited ${exitCode} (expected 0 in --auto mode); see the log above.")
|
|
97
|
+
}
|
|
98
|
+
} catch (Exception e) {
|
|
99
|
+
logger.warn("@flareapp/react-native-sourcemaps: could not run the upload CLI (${e.message}); skipping. Your release will report minified stack traces until the sourcemap is uploaded.")
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flareapp/react-native-sourcemaps",
|
|
3
|
+
"version": "2.6.0",
|
|
4
|
+
"description": "Upload React Native (Metro) 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
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"flare.gradle",
|
|
19
|
+
"scripts"
|
|
20
|
+
],
|
|
21
|
+
"main": "./dist/index.cjs",
|
|
22
|
+
"module": "./dist/index.mjs",
|
|
23
|
+
"types": "./dist/index.d.cts",
|
|
24
|
+
"bin": {
|
|
25
|
+
"flare-rn-sourcemaps": "./dist/bin.cjs"
|
|
26
|
+
},
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"import": {
|
|
30
|
+
"types": "./dist/index.d.mts",
|
|
31
|
+
"default": "./dist/index.mjs"
|
|
32
|
+
},
|
|
33
|
+
"require": {
|
|
34
|
+
"types": "./dist/index.d.cts",
|
|
35
|
+
"default": "./dist/index.cjs"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"./babel": {
|
|
39
|
+
"import": {
|
|
40
|
+
"types": "./dist/babel.d.mts",
|
|
41
|
+
"default": "./dist/babel.mjs"
|
|
42
|
+
},
|
|
43
|
+
"require": {
|
|
44
|
+
"types": "./dist/babel.d.cts",
|
|
45
|
+
"default": "./dist/babel.cjs"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"./runtime": {
|
|
49
|
+
"import": {
|
|
50
|
+
"types": "./dist/runtime.d.mts",
|
|
51
|
+
"default": "./dist/runtime.mjs"
|
|
52
|
+
},
|
|
53
|
+
"require": {
|
|
54
|
+
"types": "./dist/runtime.d.cts",
|
|
55
|
+
"default": "./dist/runtime.cjs"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"./expo": {
|
|
59
|
+
"types": "./dist/expo.d.cts",
|
|
60
|
+
"require": "./dist/expo.cjs",
|
|
61
|
+
"default": "./dist/expo.cjs"
|
|
62
|
+
},
|
|
63
|
+
"./package.json": "./package.json"
|
|
64
|
+
},
|
|
65
|
+
"engines": {
|
|
66
|
+
"node": ">=18"
|
|
67
|
+
},
|
|
68
|
+
"scripts": {
|
|
69
|
+
"prepublishOnly": "npm run build",
|
|
70
|
+
"build": "tsdown",
|
|
71
|
+
"test": "vitest run",
|
|
72
|
+
"typescript": "tsc --noEmit",
|
|
73
|
+
"release": "release-it"
|
|
74
|
+
},
|
|
75
|
+
"devDependencies": {
|
|
76
|
+
"@babel/core": "^7.0.0",
|
|
77
|
+
"@expo/config-plugins": "^56.0.9",
|
|
78
|
+
"@flareapp/flare-api": "*",
|
|
79
|
+
"@types/babel__core": "^7.0.0",
|
|
80
|
+
"tsdown": "^0.20.3",
|
|
81
|
+
"typescript": "^5.7.0",
|
|
82
|
+
"vitest": "^4.0.0",
|
|
83
|
+
"xcode": "^3.0.1"
|
|
84
|
+
},
|
|
85
|
+
"peerDependencies": {
|
|
86
|
+
"@babel/core": ">=7.0.0",
|
|
87
|
+
"@expo/config-plugins": ">=7.0.0"
|
|
88
|
+
},
|
|
89
|
+
"peerDependenciesMeta": {
|
|
90
|
+
"@babel/core": {
|
|
91
|
+
"optional": true
|
|
92
|
+
},
|
|
93
|
+
"@expo/config-plugins": {
|
|
94
|
+
"optional": true
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"publishConfig": {
|
|
98
|
+
"access": "public"
|
|
99
|
+
}
|
|
100
|
+
}
|