From 5c1c5fcf5546ba3e1c24b67d3366fb438cecc73e Mon Sep 17 00:00:00 2001 From: Jakob Scheid Date: Wed, 29 Jul 2026 20:32:31 +0200 Subject: [PATCH] feat(build): implement artifact processor --- build/build.js | 5 ++- build/utils/artifact.js | 80 +++++++++++++++++++++++++++++++++++++++++ build/utils/plugin.js | 7 ++-- 3 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 build/utils/artifact.js diff --git a/build/build.js b/build/build.js index 14e0e0f..91bf2c2 100755 --- a/build/build.js +++ b/build/build.js @@ -27,7 +27,10 @@ const files = await readdir(dir); const tryToProcessPlugin = async function tryToProcessPlugin (plugin) { if (typeof plugin.default === 'function') { - await processPlugin(plugin.default); + await processPlugin(plugin.default, { + srcBase: './src', + distBase: './dist' + }); } }; diff --git a/build/utils/artifact.js b/build/utils/artifact.js new file mode 100644 index 0000000..9c18544 --- /dev/null +++ b/build/utils/artifact.js @@ -0,0 +1,80 @@ +/* +Copyright 2026 Seekra + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { checkFileReadAccess, exists, isDirectory } from './fileAccess.js'; +import { copyFile, mkdir, writeFile } from 'node:fs/promises'; +import { dirname, join } from 'node:path'; + +const validateArtifact = function validateArtifact (artifact) { + if (typeof artifact.path !== 'string') return false; + if (artifact.type === 'text') { + return ( + typeof artifact.options.content === 'string' && + ( + typeof artifact.options.encoding === 'string' || + !Object.hasOwn(artifact.options.encoding, 'encoding') + ) + ); + } else if (artifact.type === 'copy') { + if (typeof artifact.options.source !== 'string') return false; + return checkFileReadAccess(artifact.options.source); + } + return valid; +}; + +const checkPath = async function checkPath (path) { + const directory = dirname(path); + if (await exists(directory) && !await isDirectory(directory)) { + console.warn('Skipping artifact as parent directory exists and is not a directory.'); + return false; + } else { + try { + if (!await exists(directory)) { + await mkdir(directory, { recursive: true }); + } + return true; + } catch (err) { + console.warn('Skipping artifact as directory is not creatable.'); + return false; + } + } +}; + +export const processArtifact = async function processArtifact (artifact, { srcBase = '.', distBase = '.' } = {}) { + if (!validateArtifact(artifact)) { + console.warn('Skipping artifact as it is invalid.'); + return; + } + const distPath = join(distBase, artifact.path) + if (!await checkPath(distPath)) return; + if (artifact.type === 'text') { + await writeFile( + distPath, + artifact.options.content, + { + encoding: artifact.options.encoding ?? 'utf-8' + } + ); + console.log(`Wrote to ${distPath}`); + } else if (artifact.type === 'copy') { + const srcPath = join(srcBase, artifact.options.source); + await copyFile( + srcPath, + distPath + ); + console.log(`Copied ${srcPath} -> ${distPath}`); + } +}; \ No newline at end of file diff --git a/build/utils/plugin.js b/build/utils/plugin.js index cd755ab..c6aaf39 100644 --- a/build/utils/plugin.js +++ b/build/utils/plugin.js @@ -14,12 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { processArtifact } from './artifact.js'; + const runPlugin = async function runPlugin (plugin) { const result = plugin(); if (result instanceof Promise) return await result; else return result; }; -export const processPlugin = async function processPlugin (plugin) { - await runPlugin(plugin); +export const processPlugin = async function processPlugin (plugin, options) { + const { artifacts } = await runPlugin(plugin); + for (const artifact of artifacts) await processArtifact(artifact, options); }; \ No newline at end of file