(_program: Command)
| 14 | |
| 15 | /** Creates the split action - splits a multi-notebook .deepnote file into separate files. */ |
| 16 | export function createSplitAction(_program: Command): (path: string, options: SplitOptions) => Promise<void> { |
| 17 | return async (inputPath, options) => { |
| 18 | try { |
| 19 | const { absolutePath } = await resolvePathToDeepnoteFile(inputPath) |
| 20 | debug(`Splitting: ${absolutePath}`) |
| 21 | |
| 22 | const content = await fs.readFile(absolutePath, 'utf-8') |
| 23 | const file = deserializeDeepnoteFile(content) |
| 24 | |
| 25 | const sourceDir = dirname(absolutePath) |
| 26 | const sourceStem = basename(absolutePath, '.deepnote') |
| 27 | const splits = splitByNotebooks(file, sourceStem) |
| 28 | const c = getChalk() |
| 29 | if (splits.length === 0 || (splits.length === 1 && file.project.notebooks.length === 1)) { |
| 30 | output(c.yellow('File contains only one notebook — nothing to split.')) |
| 31 | return |
| 32 | } |
| 33 | |
| 34 | const outputDir = options.output ? resolve(options.output) : sourceDir |
| 35 | |
| 36 | await fs.mkdir(outputDir, { recursive: true }) |
| 37 | |
| 38 | // Each split entry is its own single-notebook file — see splitByNotebooks docs. |
| 39 | const writtenFiles: string[] = [] |
| 40 | const force = Boolean(options.force) |
| 41 | try { |
| 42 | for (const split of splits) { |
| 43 | const outPath = join(outputDir, split.outputFilename) |
| 44 | const yaml = serializeDeepnoteFile(split.file) |
| 45 | try { |
| 46 | await fs.writeFile(outPath, yaml, { encoding: 'utf-8', flag: force ? 'w' : 'wx' }) |
| 47 | } catch (err) { |
| 48 | if (!force && isErrnoException(err, 'EEXIST')) { |
| 49 | throw new Error(`Output file already exists: ${outPath}. Use --force to overwrite.`) |
| 50 | } |
| 51 | throw err |
| 52 | } |
| 53 | writtenFiles.push(outPath) |
| 54 | output(` ${c.green('✓')} ${basename(outPath)}`) |
| 55 | } |
| 56 | } catch (err) { |
| 57 | if (!force) { |
| 58 | await Promise.allSettled(writtenFiles.map(file => fs.rm(file, { force: true }))) |
| 59 | } |
| 60 | throw err |
| 61 | } |
| 62 | |
| 63 | output(`\n${c.green('✓')} Split into ${writtenFiles.length} files`) |
| 64 | } catch (error) { |
| 65 | const message = error instanceof Error ? error.message : String(error) |
| 66 | const exitCode = error instanceof FileResolutionError ? ExitCode.InvalidUsage : ExitCode.Error |
| 67 | logError(message) |
| 68 | process.exit(exitCode) |
| 69 | } |
| 70 | } |
| 71 | } |
no test coverage detected