Files
Ant-Browser/backend/internal/automation/script_package_validator_bundle.go
ant-black 70132417ad publish: 1.2.0 snapshot (f3d7ec5)
channel: master

version: 1.2.0

source-ref: D:\code\open_source\ant-chrome master

source-commit: f3d7ec5

merged-public-base: 3d264eb
2026-05-05 18:08:10 +08:00

111 lines
3.2 KiB
Go

package automation
import (
"encoding/json"
"fmt"
"path"
)
func validateImportedBundle(record ScriptRecord, files []ImportedBundleFile) error {
fileIndex, err := buildImportedBundleFileIndex(record, files)
if err != nil {
return err
}
entryFile, err := normalizeBundleFilePath(record.EntryFile)
if err != nil {
return fmt.Errorf("脚本入口文件无效: %w", err)
}
if !isSupportedScriptModuleFile(entryFile) {
return fmt.Errorf("脚本入口文件必须是 .js / .cjs / .mjs,当前为 %s", path.Ext(entryFile))
}
if err := validateImportedPackageJSONFiles(fileIndex); err != nil {
return err
}
return validateReachableScriptModules(entryFile, fileIndex)
}
func buildImportedBundleFileIndex(record ScriptRecord, files []ImportedBundleFile) (map[string][]byte, error) {
fileIndex := make(map[string][]byte, len(files)+1)
for _, file := range files {
relativePath, err := normalizeBundleFilePath(file.Path)
if err != nil {
return nil, err
}
if containsNodeModulesPath(relativePath) {
return nil, fmt.Errorf("脚本包不能包含 node_modules,请改成自包含脚本包")
}
fileIndex[relativePath] = file.Content
}
entryFile, err := normalizeBundleFilePath(record.EntryFile)
if err != nil {
return nil, err
}
if _, exists := fileIndex[entryFile]; !exists {
fileIndex[entryFile] = []byte(record.ScriptText)
}
return fileIndex, nil
}
func validateImportedPackageJSONFiles(fileIndex map[string][]byte) error {
for filePath, content := range fileIndex {
if path.Base(filePath) != "package.json" {
continue
}
var pkg importedPackageJSON
if err := json.Unmarshal(content, &pkg); err != nil {
return fmt.Errorf("%s 不是合法的 package.json: %w", filePath, err)
}
switch {
case len(pkg.Dependencies) > 0:
return fmt.Errorf("%s 包含 dependencies,当前脚本包不支持外部 npm 依赖", filePath)
case len(pkg.DevDependencies) > 0:
return fmt.Errorf("%s 包含 devDependencies,当前脚本包不支持依赖安装流程", filePath)
case len(pkg.PeerDependencies) > 0:
return fmt.Errorf("%s 包含 peerDependencies,当前脚本包不支持外部 npm 依赖", filePath)
case len(pkg.OptionalDependencies) > 0:
return fmt.Errorf("%s 包含 optionalDependencies,当前脚本包不支持外部 npm 依赖", filePath)
}
}
return nil
}
func validateReachableScriptModules(entryFile string, fileIndex map[string][]byte) error {
queue := []string{entryFile}
visited := make(map[string]struct{}, len(fileIndex))
for len(queue) > 0 {
current := queue[len(queue)-1]
queue = queue[:len(queue)-1]
if _, seen := visited[current]; seen {
continue
}
visited[current] = struct{}{}
content, exists := fileIndex[current]
if !exists {
return fmt.Errorf("脚本入口 %s 不存在", current)
}
if !isSupportedScriptModuleFile(current) {
continue
}
for _, specifier := range extractImportedModuleSpecifiers(string(content)) {
resolved, err := validateImportedSpecifier(current, specifier, fileIndex)
if err != nil {
return fmt.Errorf("%s: %w", current, err)
}
if resolved != "" && isSupportedScriptModuleFile(resolved) {
queue = append(queue, resolved)
}
}
}
return nil
}