( scope: SourceSaveScope, filePath: string, folderPath?: string, )
| 204 | } |
| 205 | |
| 206 | export function createSourceSaveCapability( |
| 207 | scope: SourceSaveScope, |
| 208 | filePath: string, |
| 209 | folderPath?: string, |
| 210 | ): SourceSaveCapability { |
| 211 | if (!isSourceSaveFilePath(filePath)) { |
| 212 | return disabledSourceSave("unsupported-extension"); |
| 213 | } |
| 214 | |
| 215 | const resolved = |
| 216 | scope === "folder-file" && folderPath |
| 217 | ? resolveFolderSourceFile(filePath, folderPath) |
| 218 | : resolveUserPath(filePath); |
| 219 | |
| 220 | if (!resolved) return disabledSourceSave("not-local-file"); |
| 221 | if (!existsSync(resolved)) return disabledSourceSave("missing-file"); |
| 222 | |
| 223 | try { |
| 224 | const real = realpathSync(resolved); |
| 225 | if (scope === "folder-file" && folderPath) { |
| 226 | const root = realpathSync(resolveUserPath(folderPath)); |
| 227 | if (!isWithinProjectRoot(real, root)) { |
| 228 | return disabledSourceSave("not-local-file"); |
| 229 | } |
| 230 | } |
| 231 | const stat = statSync(real); |
| 232 | if (!stat.isFile()) return disabledSourceSave("unsupported-extension"); |
| 233 | const snapshot = readSourceFileSnapshot(real); |
| 234 | return enabledSourceSave(scope, real, snapshot); |
| 235 | } catch { |
| 236 | return disabledSourceSave("unreadable-file"); |
| 237 | } |
| 238 | } |
| 239 | |
| 240 | export function saveSourceFileAtomic( |
| 241 | filePath: string, |
no test coverage detected