diff --git a/code/build/ffmpeg.wasm b/code/browser/ffmpeg.wasm similarity index 100% rename from code/build/ffmpeg.wasm rename to code/browser/ffmpeg.wasm diff --git a/code/node/archive.ts b/code/node/archive.ts index 98c346d..3433bf2 100644 --- a/code/node/archive.ts +++ b/code/node/archive.ts @@ -1,18 +1,6 @@ import { exec } from './process.js' import archiver from 'archiver' import fs from 'fs' -import { BuildCommandToDecompress7ZModel } from '../shared/index.js' - -export async function buildCommandToDecompress7z(source) { - const input = BuildCommandToDecompress7ZModel.parse(source) - return [ - `7z`, - `x`, - `"${input.input.directory}"`, - `-o`, - `"${input.output.file.path}"`, - ] -} export async function createZip({ inputDirectory, diff --git a/code/node/code.ts b/code/node/code.ts index f8c8f79..c30a1e7 100644 --- a/code/node/code.ts +++ b/code/node/code.ts @@ -1,11 +1,68 @@ +import path from 'path' import { AssemblySyntax, - ClangStyleAll, + BuildBaseFileInputOutputModel, + CallLink, IOPath, LLVM_ARCHITECTURE_CONTENT, LlvmArchitecture, + buildCommandToFormatAssembly, + buildCommandToFormatC, + buildCommandToFormatCpp, + buildCommandToFormatJava, + buildCommandToFormatKotlin, + buildCommandToFormatPython, + buildCommandToFormatRuby, + buildCommandToFormatRust, + buildCommandToFormatSwift, } from '../shared/index.js' import { exec } from './process.js' +import { transform_input_output_file } from './base.js' + +export const CALL_CODE: Record = {} +export const FIND_CODE = (input: Record) => { + const i = input.input.file.path + ? path.extname(input.input.file.path as string).replace(/^\./, '') + : input.input.file.format + ? input.input.file.format + : undefined + const o = input.output.file.path + ? path.extname(input.output.file.path as string).replace(/^\./, '') + : input.output.file.format + ? input.output.file.format + : undefined + return CALL_CODE[`/${i}/format`] +} + +function define( + lang: string, + build: (x) => Array, + run: (cmd: Array) => Promise, +) { + const IO = BuildBaseFileInputOutputModel.superRefine( + transform_input_output_file(lang, lang), + ) + + CALL_CODE[String(`/${lang}/format`)] = async (source: any) => { + const input = IO.parse(source) + const cmd = build(input) + await run(cmd) + } +} + +define('kotlin', buildCommandToFormatKotlin, runFormatKotlinCommand) +define('swift', buildCommandToFormatSwift, runFormatSwiftCommand) +define('rust', buildCommandToFormatRust, runFormatRustCommand) +define('python', buildCommandToFormatPython, runFormatPythonCommand) +define('ruby', buildCommandToFormatRuby, runFormatRubyCommand) +define( + 'assembly', + buildCommandToFormatAssembly, + runFormatAssemblyCommand, +) +define('c', buildCommandToFormatC, runFormatCCommand) +define('cpp', buildCommandToFormatCpp, runFormatCCommand) +define('java', buildCommandToFormatJava, runFormatCCommand) // CSharp: .cs // Java: .java @@ -16,23 +73,6 @@ import { exec } from './process.js' // TableGen: .td // TextProto: .textpb .pb.txt .textproto .asciipb // Verilog: .sv .svh .v .vh -export async function formatCodeWithClang({ - inputPath, - style = 'llvm', -}: { - inputPath: string - style?: ClangStyleAll['basedOnStyle'] -}) { - let styleKey - if (typeof style === 'object') { - styleKey = JSON.stringify(style).replace(/"/g, '') - } else { - styleKey = style - } - // const styleKey = CLANG_FORMAT_CONTENT[style].key - // https://clang.llvm.org/docs/ClangFormatStyleOptions.html - return await exec(`clang-format --style="${style}" "${inputPath}"`) -} export async function generateAssemblyFromLlvmIr({ inputPath, @@ -72,37 +112,30 @@ export async function generateAssemblyFromLlvmIr({ // objdump disassembly export async function runFormatKotlinCommand(cmd: Array) { await exec(cmd.join(' ')) - return cmd } export async function runFormatSwiftCommand(cmd: Array) { await exec(cmd.join(' ')) - return cmd } export async function runFormatRustCommand(cmd: Array) { await exec(cmd.join(' ')) - return cmd } export async function runFormatPythonCommand(cmd: Array) { await exec(cmd.join(' ')) - return cmd } export async function runFormatRubyCommand(cmd: Array) { await exec(cmd.join(' ')) - return cmd } export async function runFormatAssemblyCommand(cmd: Array) { await exec(cmd.join(' ')) - return cmd } export async function runFormatCCommand(cmd: Array) { await exec(cmd.join(' ')) - return cmd } export async function runSwiftCommand(command: Array) { diff --git a/code/node/document.ts b/code/node/document.ts index 75ff5da..0bcb138 100644 --- a/code/node/document.ts +++ b/code/node/document.ts @@ -1,18 +1,20 @@ import { PDFDocument } from 'pdf-lib' -import { exec, spawn } from './process.js' +import { ChildProcessError, exec, spawn } from './process.js' import tmp from 'tmp-promise' import libre from 'libreoffice-convert' import fs from 'node:fs/promises' import { BuildBaseFileInputOutputModel, - BuildCommandToConvertDocumentWithPandoc, + CALIBRE_INPUT_FORMAT_CONTENT_KEY, + CALIBRE_OUTPUT_FORMAT_CONTENT_KEY, CallLink, PANDOC_INPUT_FORMAT, PANDOC_OUTPUT_FORMAT, + buildCommandToConvertDocumentWithCalibre, buildCommandToConvertDocumentWithPandoc, + buildCommandToGenerateLatexPdf, } from '../shared/index.js' import path from 'path' -import { replaceFileExtension } from '../shared/tool.js' import { RefinementCtx, z } from 'zod' import { transform_input_output_file } from './base.js' @@ -188,32 +190,64 @@ PANDOC_INPUT_FORMAT.forEach(a => { source: any, ) => { const input = IO.parse(source) + const cmd = await buildCommandToConvertDocumentWithPandoc(input) + await execPandoc(cmd) + } + }) +}) - const cmd = await buildCommandToConvertDocumentWithPandoc({ - ...input, - }) +CALIBRE_INPUT_FORMAT_CONTENT_KEY.forEach(a => { + CALIBRE_OUTPUT_FORMAT_CONTENT_KEY.forEach(b => { + if (a === b) { + return + } - console.log(cmd.join(' ')) + const IO = BuildBaseFileInputOutputModel.superRefine( + transform_input_output_file(a, b), + ) - await execPandoc(cmd) + CALL_DOCUMENT[String(`/${a}/convert/${b}`)] = async ( + source: any, + ) => { + const input = IO.parse(source) + const cmd = buildCommandToConvertDocumentWithCalibre(input) + await execCalibre(cmd) } }) }) const IO = BuildBaseFileInputOutputModel.superRefine( - transform_input_output_file('tex', 'pdf'), + (v: any, ctx: RefinementCtx) => { + if (!v.input.file.path.endsWith(`.tex`)) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: `Input path must have extension .tex`, + path: ['input', 'file', 'path'], + }) + } + + return v + }, ) CALL_DOCUMENT[String(`/tex/convert/pdf`)] = CALL_DOCUMENT[ String(`/latex/convert/pdf`) ] = async source => { const input = IO.parse(source) - console.log(input) - // console.log( - // await generateLatexPDF({ - // ...input, - // }), - // ) + + const tmpDirectory = await tmp.dir() + + const cmd = buildCommandToGenerateLatexPdf({ + ...input, + output: { + directory: tmpDirectory, + file: { + name: 'document', + }, + }, + }) + + await execPdfLatexCommand(cmd) } export async function execLatex(cmd: Array) { @@ -244,25 +278,24 @@ export async function execPandoc(cmd: Array) { } } -export async function buildCommandToGenerateLatexPDF(source: any) { - // const iPath = inputPath ? inputPath : await tmp.tmpName() - // if (!inputPath && input) { - // await fs.writeFile(iPath, input) - // } - // const tmpDirectory = tmp.dirSync() - // const outputPath = `${tmpDirectory.name}/preview.pdf` - // const command = [ - // `pdflatex -interaction=nonstopmode -halt-on-error -output-directory ${tmpDirectory.name} -jobname=preview ${iPath}`, - // ] - // try { - // await exec(command.join(' ')) - // } catch (e) { - // const message = parseLatexError(e.data.stdout) - // const error = new Error(message) - // error.cause = 'latex' - // throw error - // } - // return outputPath +export async function execCalibre(cmd: Array) { + try { + await exec(cmd.join(' ')) + } catch (e) { + throw e + } +} + +export async function execPdfLatexCommand(cmd: Array) { + try { + await exec(cmd.join(' ')) + } catch (e) { + if (e instanceof ChildProcessError) { + const message = e.data.stdout && parseLatexError(e.data.stdout) + const error = new Error(message ?? e.data.error.message) + throw error + } + } } function parseLatexError(text: string) { diff --git a/code/node/font.ts b/code/node/font.ts index 0518dd6..2110b0a 100644 --- a/code/node/font.ts +++ b/code/node/font.ts @@ -36,12 +36,7 @@ FONT_FORMAT.forEach(a => { CALL_FONT[`/${a}/convert/${b}`] = async source => { const input = IO.parse(source) - - const cmd = await buildCommandToConvertFontWithFontForge({ - ...input, - // inputExtension: a, - // outputExtension: b, - }) + const cmd = await buildCommandToConvertFontWithFontForge(input) await execFontForge(cmd) } }) diff --git a/code/node/html.ts b/code/node/html.ts index 01167c8..6f7abd7 100644 --- a/code/node/html.ts +++ b/code/node/html.ts @@ -1,4 +1,4 @@ -import { Browser, Puppeteer } from 'puppeteer' +import { Browser } from 'puppeteer' import puppeteer from 'puppeteer-extra' // add stealth plugin and use defaults (all evasion techniques) import StealthPlugin from 'puppeteer-extra-plugin-stealth' diff --git a/code/node/image.ts b/code/node/image.ts index c68b95f..b5da256 100644 --- a/code/node/image.ts +++ b/code/node/image.ts @@ -1,10 +1,7 @@ import _ from 'lodash' import { BuildBaseFileInputOutputModel, - BuildCommandToConvertAiToSvgWithInkscape, BuildCommandToConvertAiToSvgWithInkscapeModel, - BuildCommandToProcessImage, - BuildCommandToProcessImageModel, CallLink, IMAGE_MAGICK_INPUT_FORMAT, IMAGE_MAGICK_OUTPUT_FORMAT, @@ -117,10 +114,7 @@ IMAGE_MAGICK_INPUT_FORMAT.forEach(a => { CALL_IMAGE[`/${a}/convert/${b}`] = async source => { const input = IO.parse(source) - - const cmd = buildCommandToProcessImage({ - ...input, - }) + const cmd = buildCommandToProcessImage(input) await execImageMagick(cmd) } }) @@ -131,14 +125,9 @@ INKSCAPE_EXPORT_FORMAT.forEach(format => { transform_input_output_file('ai', format), ) - CALL_IMAGE[`/ai/convert/${format}`] = async ( - source: BuildCommandToProcessImage, - ) => { + CALL_IMAGE[`/ai/convert/${format}`] = async source => { const input = IO.parse(source) - - const cmd = await buildCommandToConvertAIToSVGWithInkscape({ - ...input, - }) + const cmd = await buildCommandToConvertAIToSVGWithInkscape(input) await execInkscape(cmd) } }) diff --git a/code/node/index.ts b/code/node/index.ts index a261ff0..365fe45 100644 --- a/code/node/index.ts +++ b/code/node/index.ts @@ -2,10 +2,13 @@ import { FIND_IMAGE } from './image.js' import { FIND_FONT } from './font.js' import { FIND_VIDEO } from './video.js' import { FIND_DOCUMENT } from './document.js' +import { FIND_CODE } from './code.js' export * from '../shared/index.js' export * from './document.js' +export * from './code.js' +export * from './archive.js' export * from './image.js' export * from './video.js' export * from './archive.js' @@ -36,6 +39,13 @@ export default async function call( } break } + case 'format': { + const code = FIND_CODE(input) + if (code) { + return code(input) + } + break + } } throw new Error(`Action ${task} not yet handled`) diff --git a/code/shared/archive.ts b/code/shared/archive.ts new file mode 100644 index 0000000..a6163b4 --- /dev/null +++ b/code/shared/archive.ts @@ -0,0 +1,12 @@ +import { BuildCommandToDecompress7ZModel } from '../shared/index.js' + +export async function buildCommandToDecompress7z(source) { + const input = BuildCommandToDecompress7ZModel.parse(source) + return [ + `7z`, + `x`, + `"${input.input.directory}"`, + `-o`, + `"${input.output.file.path}"`, + ] +} diff --git a/code/shared/code.ts b/code/shared/code.ts index eb4360d..ae865b6 100644 --- a/code/shared/code.ts +++ b/code/shared/code.ts @@ -10,6 +10,7 @@ import { BuildCommandToFormatAssemblyModel, BuildCommandToFormatC, BuildCommandToFormatCModel, + BuildCommandToFormatCodeWithClangModel, BuildCommandToFormatCpp, BuildCommandToFormatCppModel, BuildCommandToFormatJava, @@ -26,9 +27,7 @@ import { BuildCommandToFormatSwiftModel, } from './type/index.js' -export function buildCommandToCompileSwift( - source: BuildCommandToCompileSwift, -) { +export function buildCommandToCompileSwift(source) { const { input, output, format } = BuildCommandToCompileSwiftModel.parse(source) @@ -46,9 +45,7 @@ export function buildCommandToCompileSwift( } // objdump disassembly -export async function buildCommandToFormatKotlin( - source: BuildCommandToFormatKotlin, -) { +export function buildCommandToFormatKotlin(source) { const { input } = BuildCommandToFormatKotlinModel.parse(source) const cmd: Array = [ getCommand('ktfmt'), @@ -57,9 +54,7 @@ export async function buildCommandToFormatKotlin( return cmd } -export async function buildCommandToFormatSwift( - source: BuildCommandToFormatSwift, -) { +export function buildCommandToFormatSwift(source) { const { input } = BuildCommandToFormatSwiftModel.parse(source) const cmd: Array = [ getCommand('swift-format'), @@ -68,9 +63,7 @@ export async function buildCommandToFormatSwift( return cmd } -export async function buildCommandToFormatRust( - source: BuildCommandToFormatRust, -) { +export function buildCommandToFormatRust(source) { const { input } = BuildCommandToFormatRustModel.parse(source) const cmd: Array = [ getCommand('rustfmt'), @@ -79,9 +72,7 @@ export async function buildCommandToFormatRust( return cmd } -export async function buildCommandToFormatPython( - source: BuildCommandToFormatPython, -) { +export function buildCommandToFormatPython(source) { const { input } = BuildCommandToFormatPythonModel.parse(source) const cmd: Array = [ getCommand('black'), @@ -90,9 +81,7 @@ export async function buildCommandToFormatPython( return cmd } -export async function buildCommandToFormatRuby( - source: BuildCommandToFormatRuby, -) { +export function buildCommandToFormatRuby(source) { const { input } = BuildCommandToFormatRubyModel.parse(source) const cmd: Array = [ getCommand('rubocop'), @@ -101,9 +90,7 @@ export async function buildCommandToFormatRuby( return cmd } -export async function buildCommandToFormatAssembly( - source: BuildCommandToFormatAssembly, -) { +export function buildCommandToFormatAssembly(source) { const { input } = BuildCommandToFormatAssemblyModel.parse(source) const cmd: Array = [ getCommand('asmfmt'), @@ -112,9 +99,7 @@ export async function buildCommandToFormatAssembly( return cmd } -export async function buildCommandToFormatC( - source: BuildCommandToFormatC, -) { +export function buildCommandToFormatC(source) { const { input } = BuildCommandToFormatCModel.parse(source) const cmd: Array = [ getCommand('clang-format'), @@ -123,9 +108,7 @@ export async function buildCommandToFormatC( return cmd } -export async function buildCommandToFormatCpp( - source: BuildCommandToFormatCpp, -) { +export function buildCommandToFormatCpp(source) { const { input } = BuildCommandToFormatCppModel.parse(source) const cmd: Array = [ getCommand('clang-format'), @@ -134,9 +117,7 @@ export async function buildCommandToFormatCpp( return cmd } -export async function buildCommandToFormatJava( - source: BuildCommandToFormatJava, -) { +export function buildCommandToFormatJava(source) { const { input } = BuildCommandToFormatJavaModel.parse(source) const cmd: Array = [ getCommand('clang-format'), @@ -149,9 +130,7 @@ export async function buildCommandToFormatJava( // target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" // target triple = "arm64-apple-macosx14.0.0" // @.str = private unnamed_addr constant [14 x i8] c"Hello, World!\00", align 1 -export async function buildCommandToCompileC( - source: BuildCommandToCompileC, -) { +export async function buildCommandToCompileC(source) { const { optimizationLevel, fastMath, input, output, format, syntax } = BuildCommandToCompileCModel.parse(source) @@ -176,9 +155,7 @@ export async function buildCommandToCompileC( return cmd } -export async function buildCommandToCompileCpp( - source: BuildCommandToCompileCpp, -) { +export async function buildCommandToCompileCpp(source) { const { optimizationLevel, fastMath, input, output, format, syntax } = BuildCommandToCompileCppModel.parse(source) @@ -219,3 +196,12 @@ export async function buildCommandToDecompileBinary() { // bash format // shfmt -l -w script.sh + +export function buildCommandToFormatCodeWithClang(source) { + const input = BuildCommandToFormatCodeWithClangModel.parse(source) + return [ + `clang-format`, + `--style="${input.style}"`, + `"${input.input.file.path}"`, + ] +} diff --git a/code/shared/command.ts b/code/shared/command.ts index 5b1396f..fd18408 100644 --- a/code/shared/command.ts +++ b/code/shared/command.ts @@ -26,6 +26,7 @@ export const COMMAND = { zip: 'zip', tar: 'tar', exiftool: 'exiftool', + 'ebook-convert': 'ebook-convert', } export type Command = keyof typeof COMMAND diff --git a/code/shared/document.ts b/code/shared/document.ts index 0d20cb8..93f16a2 100644 --- a/code/shared/document.ts +++ b/code/shared/document.ts @@ -1,8 +1,10 @@ import path from 'path' import { - BuildCommandToConvertDocumentWithPandoc, + BuildCommandToConvertDocumentWithCalibreModel, BuildCommandToConvertDocumentWithPandocModel, + BuildCommandToGenerateLatexPdfModel, } from './type/index.js' +import { getCommand } from './command.js' export const DOC = 1 @@ -18,7 +20,7 @@ export async function buildCommandToConvertDocumentWithPandoc( path.extname(output.file.path as string).slice(1) const cmd = [ - `pandoc`, + getCommand(`pandoc`), `--sandbox`, `-f`, `${inputFormat}`, @@ -30,3 +32,29 @@ export async function buildCommandToConvertDocumentWithPandoc( ] return cmd } + +export function buildCommandToGenerateLatexPdf(source: any) { + const input = BuildCommandToGenerateLatexPdfModel.parse(source) + const command = [ + getCommand(`pdflatex`), + `-interaction=nonstopmode`, + `-halt-on-error`, + `-output-directory`, + `${input.output.directory}`, + `-jobname=${input.output.file.name}`, + `${input.input.file.path}`, + ] + + return command +} + +export function buildCommandToConvertDocumentWithCalibre(source: any) { + const input = + BuildCommandToConvertDocumentWithCalibreModel.parse(source) + const cmd: Array = [ + getCommand(`ebook-convert`), + input.input.file.path, + input.output.file.path, + ] + return cmd +} diff --git a/code/shared/index.ts b/code/shared/index.ts index f49239e..5009571 100644 --- a/code/shared/index.ts +++ b/code/shared/index.ts @@ -1,3 +1,4 @@ +export * from './archive.js' export * from './document.js' export * from './image.js' export * from './video.js' diff --git a/code/shared/type/cast.ts b/code/shared/type/cast.ts index 8ef48e7..ed55889 100644 --- a/code/shared/type/cast.ts +++ b/code/shared/type/cast.ts @@ -132,6 +132,20 @@ export type BuildCommandToConvertAiToSvgWithInkscape = { } } +export type BuildCommandToConvertDocumentWithCalibre = { + input: { + file: { + path: string + } + } + output: { + file: { + path: string + format?: string + } + } +} + export type BuildCommandToConvertDocumentWithPandoc = { input: { file: { @@ -274,6 +288,15 @@ export type BuildCommandToFormatC = { } } +export type BuildCommandToFormatCodeWithClang = { + input: { + file: { + path: string + } + } + style?: ClangStyleAll +} + export type BuildCommandToFormatCpp = { input: { file: { @@ -371,7 +394,19 @@ export type BuildCommandToGenerateGifWithGifsicle = { unoptimize?: boolean } -export type BuildCommandToGenerateLatexPdf = {} +export type BuildCommandToGenerateLatexPdf = { + input: { + file: { + path: string + } + } + output: { + directory: string + file: { + name: string + } + } +} export type BuildCommandToProcessImage = { input: { diff --git a/code/shared/type/source/code.ts b/code/shared/type/source/code.ts index a4e95c6..eb11a5e 100644 --- a/code/shared/type/source/code.ts +++ b/code/shared/type/source/code.ts @@ -653,3 +653,22 @@ export const build_command_to_format_java: Form = { }, }, } + +export const build_command_to_format_code_with_clang: Form = { + form: 'form', + link: { + input: { + link: { + file: { + link: { + path: { like: 'string' }, + }, + }, + }, + }, + style: { + like: 'clang_style_all', + need: false, + }, + }, +} diff --git a/code/shared/type/source/document.ts b/code/shared/type/source/document.ts index 38c5ee1..f12d972 100644 --- a/code/shared/type/source/document.ts +++ b/code/shared/type/source/document.ts @@ -486,5 +486,50 @@ export const calibre_output_profile: List = { export const build_command_to_generate_latex_pdf: Form = { form: 'form', - link: {}, + link: { + input: { + link: { + file: { + link: { + path: { like: 'string' }, + }, + }, + }, + }, + output: { + link: { + directory: { like: 'string' }, + file: { + link: { + name: { like: 'string' }, + }, + }, + }, + }, + }, +} + +export const build_command_to_convert_document_with_calibre: Form = { + form: 'form', + link: { + input: { + link: { + file: { + link: { + path: { like: 'string' }, + }, + }, + }, + }, + output: { + link: { + file: { + link: { + path: { like: 'string' }, + format: { like: 'string', need: false }, + }, + }, + }, + }, + }, } diff --git a/code/shared/type/take.ts b/code/shared/type/take.ts index 242ae7e..29a8242 100644 --- a/code/shared/type/take.ts +++ b/code/shared/type/take.ts @@ -134,6 +134,21 @@ export const BuildCommandToConvertAiToSvgWithInkscapeModel: z.ZodType = + z.object({ + input: z.object({ + file: z.object({ + path: z.string(), + }), + }), + output: z.object({ + file: z.object({ + path: z.string(), + format: z.optional(z.string()), + }), + }), + }) + export const BuildCommandToConvertDocumentWithPandocModel: z.ZodType = z.object({ input: z.object({ @@ -367,6 +382,16 @@ export const BuildCommandToFormatCModel: z.ZodType = }), }) +export const BuildCommandToFormatCodeWithClangModel: z.ZodType = + z.object({ + input: z.object({ + file: z.object({ + path: z.string(), + }), + }), + style: z.optional(z.lazy(() => ClangStyleAllModel)), + }) + export const BuildCommandToFormatCppModel: z.ZodType = z.object({ input: z.object({ @@ -473,7 +498,19 @@ export const BuildCommandToGenerateGifWithGifsicleModel: z.ZodType = - z.object({}) + z.object({ + input: z.object({ + file: z.object({ + path: z.string(), + }), + }), + output: z.object({ + directory: z.string(), + file: z.object({ + name: z.string(), + }), + }), + }) export const BuildCommandToProcessImageModel: z.ZodType = z.object({