diff --git a/cmd/sourcemaps/upload.go b/cmd/sourcemaps/upload.go index 6c5110a2..51b04f7e 100644 --- a/cmd/sourcemaps/upload.go +++ b/cmd/sourcemaps/upload.go @@ -168,6 +168,21 @@ func runE(client resources.Client) func(cmd *cobra.Command, args []string) error } } +var sourceMapUploadSuffixes = []string{ + ".js.map", ".js", + ".jsbundle.map", ".jsbundle", + ".bundle.map", ".bundle", +} + +func isSourceMapUploadFile(name string) bool { + for _, suffix := range sourceMapUploadSuffixes { + if strings.HasSuffix(name, suffix) { + return true + } + } + return false +} + func getAllSourceMapFiles(path string) ([]SourceMapFile, error) { var files []SourceMapFile routeGroupPattern := regexp.MustCompile(`\(.+?\)/`) @@ -194,7 +209,7 @@ func getAllSourceMapFiles(path string) ([]SourceMapFile, error) { return filepath.SkipDir } - if !d.IsDir() && (strings.HasSuffix(filePath, ".js.map") || strings.HasSuffix(filePath, ".js")) { + if !d.IsDir() && isSourceMapUploadFile(filePath) { relPath, err := filepath.Rel(path, filePath) if err != nil { return err @@ -222,7 +237,7 @@ func getAllSourceMapFiles(path string) ([]SourceMapFile, error) { } if len(files) == 0 { - return nil, fmt.Errorf("no .js.map files found. Please double check that you have generated sourcemaps for your app") + return nil, fmt.Errorf("no sourcemap files found (looked for *.js.map, *.jsbundle.map, *.bundle.map and their minified files). Please double check that you have generated sourcemaps for your app") } return files, nil diff --git a/cmd/sourcemaps/upload_test.go b/cmd/sourcemaps/upload_test.go index 3f78d0cd..eba068fb 100644 --- a/cmd/sourcemaps/upload_test.go +++ b/cmd/sourcemaps/upload_test.go @@ -198,7 +198,56 @@ func TestGetAllSourceMapFiles(t *testing.T) { defer os.RemoveAll(emptyDir) _, err = getAllSourceMapFiles(emptyDir) assert.Error(t, err) - assert.Contains(t, err.Error(), "no .js.map files found") + assert.Contains(t, err.Error(), "no sourcemap files found") +} + +func TestIsSourceMapUploadFile(t *testing.T) { + // Web bundles + maps. + assert.True(t, isSourceMapUploadFile("app.js")) + assert.True(t, isSourceMapUploadFile("app.js.map")) + // React Native iOS bundle + map. + assert.True(t, isSourceMapUploadFile("main.jsbundle")) + assert.True(t, isSourceMapUploadFile("main.jsbundle.map")) + // React Native Android bundle + map. + assert.True(t, isSourceMapUploadFile("index.android.bundle")) + assert.True(t, isSourceMapUploadFile("index.android.bundle.map")) + // Unrelated files are ignored. + assert.False(t, isSourceMapUploadFile("styles.css")) + assert.False(t, isSourceMapUploadFile("styles.css.map")) + assert.False(t, isSourceMapUploadFile("README.md")) +} + +func TestGetAllSourceMapFilesReactNative(t *testing.T) { + tempDir, err := os.MkdirTemp("", "sourcemap-rn-test") + assert.NoError(t, err) + defer os.RemoveAll(tempDir) + + // The names React Native's `react-native bundle` produces. + rnFiles := []string{ + "main.jsbundle", + "main.jsbundle.map", + "index.android.bundle", + "index.android.bundle.map", + } + for _, name := range rnFiles { + err = os.WriteFile(filepath.Join(tempDir, name), []byte("{}"), 0644) + assert.NoError(t, err) + } + // A non-sourcemap file that must be skipped. + err = os.WriteFile(filepath.Join(tempDir, "assets.png"), []byte("x"), 0644) + assert.NoError(t, err) + + files, err := getAllSourceMapFiles(tempDir) + assert.NoError(t, err) + + found := make(map[string]bool) + for _, f := range files { + found[f.Name] = true + } + for _, name := range rnFiles { + assert.True(t, found[name], "expected %s to be discovered for upload", name) + } + assert.False(t, found["assets.png"], "non-sourcemap files must be skipped") } func TestGetSourceMapUploadUrlsErrors(t *testing.T) {