import {dest, series, src} from 'gulp'; import process from 'node:process'; import replace from 'gulp-replace'; import {getRandomValues} from 'node:crypto'; import * as dartSass from 'sass'; import gulpSass from 'gulp-sass'; import {deleteAsync} from 'del'; import {readdirSync, readFileSync, writeFileSync, rmdirSync, existsSync, mkdirSync, statSync} from "fs"; import {join} from 'node:path'; import {compilePack} from '@foundryvtt/foundryvtt-cli'; const sass = gulpSass(dartSass); /** * Generate a random alphanumeric string ID of a given requested length using `crypto.getRandomValues()`. * @param {number} length The length of the random string to generate, which must be at most 16384. * @returns {string} A string containing random letters (A-Z, a-z) and numbers (0-9). */ function randomID(length = 16) { const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const cutoff = 0x100000000 - (0x100000000 % chars.length); const random = new Uint32Array(length); do { getRandomValues(random); } while (random.some(x => x >= cutoff)); let id = ""; for (let i = 0; i < length; i++) id += chars[random[i] % chars.length]; return id; } const convert = function (from, to, ofType, overwrite = true) { const SOURCE = from; const DEST = to; const TYPE = ofType; if (overwrite) { try { rmdirSync(DEST, {force: true, recursive: true}) } catch (e) { } mkdirSync(DEST) } const filewalker = (source) => { console.debug("entering directory", source); readdirSync(source).forEach(file => { if (statSync(join(source, file)).isDirectory()) { filewalker(join(source, file)); } else { console.debug("processing file", join(source, file)) let originalSource = JSON.parse(readFileSync(join(source, file), {encoding: "utf8"})); let id = randomID(); let targetSource = { _id: id, _key: "!items!" + id, type: TYPE, img: originalSource.image, name: originalSource.name.trim(), system: {...originalSource}, } delete targetSource.system.image; let target = JSON.stringify(targetSource, null, 2); let newFileName = "./" + join(DEST, id + ".json"); writeFileSync(newFileName, target, {encoding: "utf8"}); } }); } filewalker(SOURCE) } function cleanDist() { return deleteAsync(['dist/**']); } function buildStyles() { return src('src/style/**/*.scss') .pipe(sass().on('error', sass.logError)) .pipe(dest('dist/style/')) } function copySource() { return src(['src/**/*', '!src/assets/**/*', '!src/style/**/*.scss', '!src/packs/**/*']) .pipe(dest('dist/')); } function copyAssets() { return src(['src/assets/**/*'], {encoding: false}) .pipe(dest('dist/assets/')) } function updateManifestFile() { return src('dist/system.json') .pipe(replace('{{VERSION}}', process.env.VERSION)) .pipe(dest('dist/')) } async function prepareDB() { try { if (!existsSync("./src/packs/__source")) { mkdirSync("./src/packs/__source"); } convert("./src/packs/_source/talente", "./src/packs/__source/talente", "Skill"); convert("./src/packs/_source/zauber", "./src/packs/__source/zauber", "Spell"); convert("./src/packs/_source/vorteile", "./src/packs/__source/vorteile", "Advantage"); convert("./src/packs/_source/nachteile", "./src/packs/__source/vorteile", "Advantage", false); convert("./src/packs/_source/sonderfertigkeiten", "./src/packs/__source/sonderfertigkeiten", "SpecialAbility"); convert("./src/packs/_source/waehrungen", "./src/packs/__source/waehrungen", "Equipment"); convert("./src/packs/_source/Gegenstaende/Waffen", "./src/packs/__source/Waffen", "Equipment"); convert("./src/packs/_source/Gegenstaende/Munition", "./src/packs/__source/Munition", "Equipment"); convert("./src/packs/_source/Gegenstaende/Ruestzeug", "./src/packs/__source/Ruestzeug", "Equipment"); convert("./src/packs/_source/Gegenstaende/Behaelter", "./src/packs/__source/Gegenstaende", "Equipment", false); convert("./src/packs/_source/Gegenstaende/Bekleidung", "./src/packs/__source/Gegenstaende", "Equipment", false); convert("./src/packs/_source/Gegenstaende/Beleuchtung", "./src/packs/__source/Gegenstaende", "Equipment", false); convert("./src/packs/_source/Gegenstaende/Buecher", "./src/packs/__source/Gegenstaende", "Equipment", false); convert("./src/packs/_source/Gegenstaende/Essutensilien", "./src/packs/__source/Gegenstaende", "Equipment", false); convert("./src/packs/_source/Gegenstaende/Sonstiges", "./src/packs/__source/Gegenstaende", "Equipment", false); convert("./src/packs/_source/Gegenstaende/Werkzeug", "./src/packs/__source/Gegenstaende", "Equipment", false); convert("./src/packs/_source/Gegenstaende/Seile", "./src/packs/__source/Gegenstaende", "Equipment", false); convert("./src/packs/_source/liturgien-und-segnungen", "./src/packs/__source/liturgien", "Liturgy"); convert("./src/packs/_source/wunden", "./src/packs/__source/wunden", "ActiveEffect"); convert("./src/packs/_source/kulturen", "./src/packs/__source/kulturen", "Culture"); convert("./src/packs/_source/spezien", "./src/packs/__source/spezien", "Species"); convert("./src/packs/_source/professionen", "./src/packs/__source/professionen", "Profession"); } catch (err) { console.error(err); } } function buildDB() { // Determine which source folders to process const PACK_SRC = "src/packs/__source" const PACK_DEST = "dist/packs/" return new Promise(async (resolve, reject) => { const folders = readdirSync(PACK_SRC, {withFileTypes: true}).filter(file => file.isDirectory() ); for (const folder of folders) { const src = join(PACK_SRC, folder.name); const dest = join(PACK_DEST, folder.name); console.info(`Compiling pack ${folder.name}`); await compilePack(src, dest, {recursive: true, nedb: false}); } resolve() }) } export default series( cleanDist, copySource, copyAssets, buildStyles, prepareDB, buildDB, updateManifestFile )