diff --git a/server-node/tsconfig.json b/server-node/tsconfig.json index 61886b3e6..68eb819a8 100644 --- a/server-node/tsconfig.json +++ b/server-node/tsconfig.json @@ -11,7 +11,7 @@ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ - // "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ diff --git a/shared-operations/src/workflow/listOperatorsInDir.ts b/shared-operations/src/compiletime/operatorDescription.ts similarity index 67% rename from shared-operations/src/workflow/listOperatorsInDir.ts rename to shared-operations/src/compiletime/operatorDescription.ts index a0c28585b..a831353f4 100644 --- a/shared-operations/src/workflow/listOperatorsInDir.ts +++ b/shared-operations/src/compiletime/operatorDescription.ts @@ -5,31 +5,35 @@ import { CompileTimeFunctionResult, } from "vite-plugin-compile-time" -function getAllJsFiles(directory) { + async function obtainOperatorInformation(directory) { const jsFiles = []; // Synchronously read the contents of the directory const files = fs.readdirSync(directory); // Iterate through the files and filter out the JavaScript files - files.forEach((file) => { + for (const file of files) { const filePath = path.join(directory, file) const isJsFile = fs.statSync(filePath).isFile() && path.extname(filePath) === '.ts'; if (isJsFile) { const baseName = path.basename(filePath, '.ts'); if(baseName != "index") { - jsFiles.push(baseName); + //TODO: Extract more info from operators. Currently not possible see: https://github.com/egoist/vite-plugin-compile-time/issues/25 + + jsFiles.push({ + baseName: baseName + }); } } - }); + } return jsFiles; } export default async ( args: CompileTimeFunctionArgs, ): Promise => { - const jsFiles = getAllJsFiles(__dirname + "/../functions/"); + const jsFiles = await obtainOperatorInformation(__dirname + "/../functions/"); return { data: jsFiles, // Trigger rebuild when watched files change diff --git a/shared-operations/src/functions/impose.schema.ts b/shared-operations/src/functions/impose.schema.ts new file mode 100644 index 000000000..1650479d4 --- /dev/null +++ b/shared-operations/src/functions/impose.schema.ts @@ -0,0 +1,59 @@ +import { OperatorSchema } from "."; +import Joi from "@stirling-tools/joi"; +import { JoiPDFFileSchema } from "../wrappers/PdfFileJoi"; + +import i18next from "i18next"; + +export default new OperatorSchema( + i18next.t("friendlyName", { ns: "impose" }), + i18next.t("description", { ns: "impose" }), + JoiPDFFileSchema.label(i18next.t("inputs.pdffile.name")).description(i18next.t("inputs.pdffile.description")), + Joi.object({ + nup: Joi.number().integer().valid(2, 3, 4, 8, 9, 12, 16).required() + .label(i18next.t("values.nup.friendlyName", { ns: "impose" })).description(i18next.t("values.nup.description", { ns: "impose" })) + .example("3").example("4"), + format: Joi.string().valid(...[ + // ISO 216:1975 A + "4A0", "2A0", "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "A10", + + // ISO 216:1975 B + "B0+", "B0", "B1+", "B1", "B2+", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "B10", + + // ISO 269:1985 C + "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "C10", + + // ISO 217:2013 untrimmed + "RA0", "RA1", "RA2", "RA3", "RA4", "SRA0", "SRA1", "SRA2", "SRA3", "SRA4", "SRA1+", "SRA2+", "SRA3+", "SRA3++", + + // American + "SuperB", "Tabloid", "Legal", "GovLegal", "Letter", "GovLetter", "Executive", "HalfLetter", "JuniorLegal", "Photo", + + // ANSI/ASME Y14.1 + "ANSIA", "ANSIB", "ANSIC", "ANSID", "ANSIE", "ANSIF", + + // ANSI/ASME Y14.1 Architectural series + "ARCHA", "ARCHB", "ARCHC", "ARCHD", "ARCHE", "ARCHE1", "ARCHE2", "ARCHE3", + + // American uncut + "Bond", "Book", "Cover", "Index", "NewsPrint", "Offset", + + // English uncut + "Crown", "DoubleCrown", "Quad", "Demy", "DoubleDemy", "Medium", "Royal", "SuperRoyal", + "DoublePott", "DoublePost", "Foolscap", "DoubleFoolscap", + + // F4 + + // China GB/T 148-1997 D Series + "D0", "D1", "D2", "D3", "D4", "D5", "D6", + "RD0", "RD1", "RD2", "RD3", "RD4", "RD5", "RD6", + + // Japan + "JIS-B0", "JIS-B1", "JIS-B2", "JIS-B3", "JIS-B4", "JIS-B5", "JIS-B6", + "JIS-B7", "JIS-B8", "JIS-B9", "JIS-B10", "JIS-B11", "JIS-B12", + "Shirokuban4", "Shirokuban5", "Shirokuban6", "Kiku4", "Kiku5", "AB", "B40", "Shikisen" + ].flatMap(size => [size, size + "P", size + "L"])).required() + .label(i18next.t("values.format.friendlyName", { ns: "impose" })).description(i18next.t("values.format.description", { ns: "impose" })) + .example("A4").example("A3L") + }), + JoiPDFFileSchema.label(i18next.t("outputs.pdffile.name")).description(i18next.t("outputs.pdffile.description")) +) \ No newline at end of file diff --git a/shared-operations/src/functions/impose.ts b/shared-operations/src/functions/impose.ts index 0b3f7a885..420b81ceb 100644 --- a/shared-operations/src/functions/impose.ts +++ b/shared-operations/src/functions/impose.ts @@ -2,78 +2,9 @@ import { PdfFile, RepresentationType } from "../wrappers/PdfFile"; import { Operator, Progress, oneToOne } from "."; -import Joi from "@stirling-tools/joi"; -import { JoiPDFFileSchema } from "../wrappers/PdfFileJoi"; - -import i18next from "i18next"; - import * as pdfcpuWrapper from "#pdfcpu"; // This is updated by tsconfig.json/paths for the context (browser, node, etc.) this module is used in. - export class Impose extends Operator { - static type = "impose"; - - /** - * Validation & Localisation - */ - - protected static inputSchema = JoiPDFFileSchema.label(i18next.t("inputs.pdffile.name")).description(i18next.t("inputs.pdffile.description")); - protected static valueSchema = Joi.object({ - nup: Joi.number().integer().valid(2, 3, 4, 8, 9, 12, 16).required() - .label(i18next.t("values.nup.friendlyName", { ns: "impose" })).description(i18next.t("values.nup.description", { ns: "impose" })) - .example("3").example("4"), - format: Joi.string().valid(...[ - // ISO 216:1975 A - "4A0", "2A0", "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "A10", - - // ISO 216:1975 B - "B0+", "B0", "B1+", "B1", "B2+", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "B10", - - // ISO 269:1985 C - "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "C10", - - // ISO 217:2013 untrimmed - "RA0", "RA1", "RA2", "RA3", "RA4", "SRA0", "SRA1", "SRA2", "SRA3", "SRA4", "SRA1+", "SRA2+", "SRA3+", "SRA3++", - - // American - "SuperB", "Tabloid", "Legal", "GovLegal", "Letter", "GovLetter", "Executive", "HalfLetter", "JuniorLegal", "Photo", - - // ANSI/ASME Y14.1 - "ANSIA", "ANSIB", "ANSIC", "ANSID", "ANSIE", "ANSIF", - - // ANSI/ASME Y14.1 Architectural series - "ARCHA", "ARCHB", "ARCHC", "ARCHD", "ARCHE", "ARCHE1", "ARCHE2", "ARCHE3", - - // American uncut - "Bond", "Book", "Cover", "Index", "NewsPrint", "Offset", - - // English uncut - "Crown", "DoubleCrown", "Quad", "Demy", "DoubleDemy", "Medium", "Royal", "SuperRoyal", - "DoublePott", "DoublePost", "Foolscap", "DoubleFoolscap", - - // F4 - - // China GB/T 148-1997 D Series - "D0", "D1", "D2", "D3", "D4", "D5", "D6", - "RD0", "RD1", "RD2", "RD3", "RD4", "RD5", "RD6", - - // Japan - "JIS-B0", "JIS-B1", "JIS-B2", "JIS-B3", "JIS-B4", "JIS-B5", "JIS-B6", - "JIS-B7", "JIS-B8", "JIS-B9", "JIS-B10", "JIS-B11", "JIS-B12", - "Shirokuban4", "Shirokuban5", "Shirokuban6", "Kiku4", "Kiku5", "AB", "B40", "Shikisen" - ].flatMap(size => [size, size + "P", size + "L"])).required() - .label(i18next.t("values.format.friendlyName", { ns: "impose" })).description(i18next.t("values.format.description", { ns: "impose" })) - .example("A4").example("A3L") - }); - protected static outputSchema = JoiPDFFileSchema.label(i18next.t("outputs.pdffile.name")).description(i18next.t("outputs.pdffile.description")); - - static schema = Joi.object({ - input: Impose.inputSchema, - values: Impose.valueSchema.required(), - output: Impose.outputSchema - }).label(i18next.t("friendlyName", { ns: "impose" })).description(i18next.t("description", { ns: "impose" })); - - /** * Logic */ diff --git a/shared-operations/src/functions/index.ts b/shared-operations/src/functions/index.ts index 0f2ac4a4c..4aa7db9ea 100644 --- a/shared-operations/src/functions/index.ts +++ b/shared-operations/src/functions/index.ts @@ -15,20 +15,7 @@ export interface Progress { } export class Operator { - /** The internal name of the operator in camelCase (impose, merge, etc.) */ - static type: string; - - /** The Joi validators & decorators */ - protected static inputSchema: Joi.Schema; - protected static valueSchema: Joi.Schema; - protected static outputSchema: Joi.Schema; - static schema: Joi.ObjectSchema<{ - input: any; - values: any; - output: any; - }>; - - actionValues: any; + actionValues: any = undefined; constructor (action: Action) { this.actionValues = action.values; @@ -40,6 +27,19 @@ export class Operator { } } + +export class OperatorSchema { + schema: Joi.ObjectSchema; + + constructor(label: string, description: string, inputSchema: Joi.Schema, valueSchema: Joi.Schema, outputSchema: Joi.Schema) { + this.schema = Joi.object({ + input: inputSchema, + values: valueSchema.required(), + output: outputSchema + }).label(label).description(description); + } +} + /** This function should be used if the Operation may take multiple files as inputs and only outputs one file */ export async function nToOne (inputs: I[], callback: (input: I[]) => Promise): Promise { return [await callback(inputs)]; diff --git a/shared-operations/src/workflow/operatorAccessor.ts b/shared-operations/src/workflow/operatorAccessor.ts index 245321170..9729a9287 100644 --- a/shared-operations/src/workflow/operatorAccessor.ts +++ b/shared-operations/src/workflow/operatorAccessor.ts @@ -1,11 +1,11 @@ import { Operator } from "../functions"; import i18next from "i18next"; -const compileTimeOperatorList: string[] = import.meta.compileTime("./listOperatorsInDir.ts"); // The will compile to ["impose", "extractPages", etc...] +const compileTimeOperatorList: {basename: string}[] = import.meta.compileTime("../compiletime/operatorDescription.ts"); // The will compile to ["impose", "extractPages", etc...] export async function getOperatorByName(name: string): Promise { // Check if exists - if(!compileTimeOperatorList.includes(name)) return; + if(!compileTimeOperatorList.find(e => e.basename == name)) return; i18next.loadNamespaces(name, (err, t) => { if (err) throw err; console.log(t) }); const loadedModule = await import("../functions/" + name + ".ts"); @@ -17,8 +17,7 @@ export async function getOperatorByName(name: string): Promise e.basename); return availableOperators; } diff --git a/shared-operations/src/workflow/validateOperations.ts b/shared-operations/src/workflow/validateOperations.ts index 3757c0549..da036a401 100644 --- a/shared-operations/src/workflow/validateOperations.ts +++ b/shared-operations/src/workflow/validateOperations.ts @@ -20,7 +20,7 @@ export async function validateOperations(actions: Action[]): Promise<{ valid: bo } const validationResult = operator.schema.validate({values: action.values}); - // TODO: convert everything to joiresult format + // TODO: convert everything to joiresult format instead of returning a new format if(validationResult.error) { return { valid: false, reason: validationResult.error.message}; } diff --git a/shared-operations/src/wrappers/PdfFile.ts b/shared-operations/src/wrappers/PdfFile.ts index dc748e053..89c09911d 100644 --- a/shared-operations/src/wrappers/PdfFile.ts +++ b/shared-operations/src/wrappers/PdfFile.ts @@ -5,6 +5,7 @@ if(isBrowser){ PDFJS.GlobalWorkerOptions.workerSrc = pdfJSWorkerURL; } +//TODO: dynamically import these libs. import type { PDFDocumentProxy as PDFJSDocument } from "pdfjs-dist/types/src/display/api"; import { PDFDocument as PDFLibDocument } from "pdf-lib";