@goliapkg/sentori-cli 0.2.1 → 0.4.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.
@@ -0,0 +1,87 @@
1
+ import { spawnSync } from 'node:child_process'
2
+ import { existsSync, mkdtempSync, rmSync } from 'node:fs'
3
+ import { createRequire } from 'node:module'
4
+ import { tmpdir } from 'node:os'
5
+ import { join } from 'node:path'
6
+
7
+ import { uploadSourcemaps } from './upload.js'
8
+
9
+ /** Resolve `react-native/scripts/compose-source-maps.js` from the
10
+ * current project's node_modules. Returns null if react-native isn't
11
+ * installed or the version doesn't ship that script. */
12
+ export function resolveComposeScript(fromDir = process.cwd()): null | string {
13
+ const req = createRequire(join(fromDir, 'noop.js'))
14
+ for (const id of [
15
+ 'react-native/scripts/compose-source-maps.js',
16
+ '@react-native/community-cli-plugin/dist/utils/composeSourceMaps.js',
17
+ ]) {
18
+ try {
19
+ const p = req.resolve(id)
20
+ if (existsSync(p)) return p
21
+ } catch {
22
+ // try next
23
+ }
24
+ }
25
+ return null
26
+ }
27
+
28
+ /** Compose a Metro packager source map + a Hermes source map into a
29
+ * single map (a temp file the caller is responsible for deleting).
30
+ * Throws if react-native's compose script can't be found or fails. */
31
+ export function composeSourceMaps(metroMap: string, hermesMap: string): string {
32
+ for (const p of [metroMap, hermesMap]) {
33
+ if (!existsSync(p)) throw new Error(`no such file: ${p}`)
34
+ }
35
+ const script = resolveComposeScript()
36
+ if (!script) {
37
+ throw new Error(
38
+ "couldn't find react-native's compose-source-maps.js — install react-native, or " +
39
+ 'compose the maps yourself and use `sentori-cli upload sourcemap <composed.map>`',
40
+ )
41
+ }
42
+ const out = join(mkdtempSync(join(tmpdir(), 'sentori-rn-')), 'composed.map')
43
+ const r = spawnSync('node', [script, metroMap, hermesMap, '-o', out], { stdio: 'inherit' })
44
+ if (r.status !== 0) {
45
+ throw new Error(`compose-source-maps.js exited with ${r.status ?? 'signal'}`)
46
+ }
47
+ if (!existsSync(out)) throw new Error('compose-source-maps.js produced no output')
48
+ return out
49
+ }
50
+
51
+ export type RnUploadOptions = {
52
+ apiUrl: string
53
+ /** Optional bundle file (.jsbundle / .bundle) to upload alongside the map. */
54
+ bundle?: string
55
+ dryRun?: boolean
56
+ hermesMap: string
57
+ metroMap: string
58
+ release: string
59
+ token: string
60
+ }
61
+
62
+ /** Compose the Metro + Hermes maps, then upload the result (and the
63
+ * bundle, if given). Cleans up the temp composed map. */
64
+ export async function reactNativeUpload(opts: RnUploadOptions): Promise<{
65
+ files: string[]
66
+ uploaded?: number
67
+ }> {
68
+ const composed = composeSourceMaps(opts.metroMap, opts.hermesMap)
69
+ try {
70
+ const paths = [composed, ...(opts.bundle ? [opts.bundle] : [])]
71
+ const r = await uploadSourcemaps({
72
+ apiUrl: opts.apiUrl,
73
+ dryRun: opts.dryRun,
74
+ paths,
75
+ release: opts.release,
76
+ token: opts.token,
77
+ })
78
+ return { files: r.files, uploaded: r.uploaded }
79
+ } finally {
80
+ try {
81
+ rmSync(composed, { force: true })
82
+ rmSync(join(composed, '..'), { force: true, recursive: true })
83
+ } catch {
84
+ // best-effort cleanup
85
+ }
86
+ }
87
+ }