func loadSourceFromPaths()

in sg/internal/source/fs.go [64:163]


func loadSourceFromPaths(contextRoot string, paths []string) ([]Source, error) {
	// when contextRoot specified, all paths must be relative to contextRoot.
	// FIXME(hbc): this implementation may not be correct in Windows (see context in `filepath.HasPrefix`)
	//             We should revisit this in later changes.
	if contextRoot != "" {
		contextRootAbs, err := filepath.Abs(contextRoot)
		if err != nil {
			return nil, fmt.Errorf("failed to get absolute path of context root %q: %w", contextRoot, err)
		}

		for _, p := range paths {
			pAbs, err := filepath.Abs(p)
			if err != nil {
				return nil, fmt.Errorf("failed to get absolute path of %q: %w", p, err)
			}

			if !strings.HasPrefix(pAbs, contextRootAbs) {
				return nil, fmt.Errorf("path %q is not relative to context root %q", p, contextRoot)
			}
		}
	}
	relativeToContextRoot := relativeToContextRootFn(contextRoot)

	var files []string
	var jsonFiles []string

	walk := func(path string, info fs.DirEntry, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		if parser.FileSupported(path) {
			if strings.EqualFold(filepath.Ext(path)[1:], parser.JSON) {
				jsonFiles = append(jsonFiles, path)
			} else {
				files = append(files, path)
			}
		}

		return nil
	}

	for _, path := range paths {
		if err := filepath.WalkDir(path, walk); err != nil {
			return nil, fmt.Errorf("walk path %q: %w", path, err)
		}
	}

	if len(files)+len(jsonFiles) < 1 {
		return nil, fmt.Errorf("no files found from given paths: %v", paths)
	}

	// parse json files with jsonc to allow comments
	jsonConfigurations, err := parser.ParseConfigurationsAs(jsonFiles, parser.JSONC)
	if err != nil {
		return nil, fmt.Errorf("parse configurations: %w", err)
	}
	configurations, err := parser.ParseConfigurations(files)
	if err != nil {
		return nil, fmt.Errorf("parse configurations: %w", err)
	}
	maps.Copy(configurations, jsonConfigurations)

	filePathsSorted := make([]string, 0, len(configurations))
	for filePath := range configurations {
		filePathsSorted = append(filePathsSorted, filePath)
	}
	sort.Strings(filePathsSorted)

	var rv []Source
	for _, filePath := range filePathsSorted {
		c := configurations[filePath]
		var subConfigurations []any
		if cc, ok := c.([]any); ok {
			subConfigurations = cc
		} else {
			subConfigurations = []any{c}
		}

		var parsedConfigurations []ast.Value
		for _, rawConfiguration := range subConfigurations {
			parsedConfiguration, err := parseRawConfiguration(rawConfiguration)
			if err != nil {
				return nil, fmt.Errorf("parse raw configuration: %w", err)
			}
			parsedConfigurations = append(parsedConfigurations, parsedConfiguration)
		}

		rv = append(rv, &fsSource{
			filePath:       relativeToContextRoot(filePath),
			configurations: parsedConfigurations,
		})
	}

	return rv, nil
}