UNPKG

minecraft-utils-shared

Version:

Shared utils for Minecraft Bedrock / Forge development related utilities.

238 lines (219 loc) 6.59 kB
/** * @file Minecraft Utils Shared - Template file * @license Apache-2.0 * @author Markus@Bordihn.de (Markus Bordihn) */ import chalk from 'chalk'; import fs from 'fs'; import path from 'path'; const fileMarker = '+++'; const positionMarker = '@@@'; /** * @enum */ const fileType = Object.freeze({ JAVA: 'java', RESOURCE: 'resource', DATA: 'data', DATA_MINECRAFT: 'data_minecraft', UNKNOWN: 'unknown', }); const resourceLocations = [ 'blockstates', 'font', 'lang', 'models', 'particles', 'shaders', 'texts', 'textures', ]; const dataLocations = [ 'advancements', 'loot_tables', 'recipes', 'structures', 'tags', ]; /** * @param {string} file */ const read = (file) => { if (!fs.existsSync(file)) { console.error(chalk.red('Unable to find template file', file)); return; } const fileContent = fs.readFileSync(file, 'utf8'); try { return fileContent; } catch (error) { console.error(chalk.red('Unable to parse template file', file, ':', error)); } }; /** * @param {string} file * @param {object} placeholder */ const parse = (file, placeholder = {}) => { const data = []; let content = read(file); if ( !content || !content.includes(fileMarker) || !content.includes(positionMarker) ) { console.warn(chalk.yellow('Template file has no valid content', file)); return; } // Converting placeholders, if needed. if (Object.keys(placeholder).length > 0) { content = content.replace( /\[\[ --([A-Za-z0-9_. ]+)-- \]\]/g, (matchString) => { const placeholderString = matchString .replace('[[ --', '') .replace('-- ]]', '') .trim(); return placeholder[placeholderString] || matchString; } ); } // Parse template patch entries. const fileEntryParts = content.split(fileMarker); fileEntryParts.forEach((filePart) => { if (filePart) { const fileName = filePart.split(/\r\n|\n|\r/, 1)[0].trim(); const patchParts = filePart.substring( filePart.indexOf(positionMarker) + positionMarker.length ); const patchPart = patchParts.split(positionMarker); const positionInstruction = patchPart[0].trim(); const code = patchPart[1] .replace(/^\r\n|\n|\r/, '') .replace(/(\r\n|\n|\r){2,}$/, '\r\n'); const patchData = { fileName: fileName, filePath: path.join.apply(null, fileName.split('/')), fileType: getFileType(fileName), code: code, }; if (positionInstruction.startsWith('before:')) { patchData['before'] = positionInstruction.replace('before:', '').trim(); } else if (positionInstruction.startsWith('after:')) { patchData['after'] = positionInstruction.replace('after:', '').trim(); } else if (positionInstruction.startsWith('create:')) { patchData['create'] = positionInstruction.replace('create:', '').trim(); } else if (positionInstruction == 'create') { patchData['create'] = true; } else if (positionInstruction.startsWith('copy:')) { let targetFile = positionInstruction.replace('copy:', '').trim(); if (targetFile.startsWith('/') && !targetFile.startsWith('//')) { const baseTemplatePath = getBaseTemplatePath(file); if (baseTemplatePath) { targetFile = path.join( baseTemplatePath, path.join.apply(null, targetFile.substring(1).split('/')) ); } } else { targetFile = path.join.apply(null, targetFile.split('/')); } patchData['copy'] = targetFile; } data.push(patchData); } }); return data; }; /** * @param {string} template */ const getBaseTemplatePath = (template) => { let result = ''; const fullTemplatePath = path.resolve(template); const templatePath = fullTemplatePath.split(path.sep).join('/'); if (templatePath.includes('/templates/java/')) { result = templatePath.substring( 0, templatePath.indexOf('/templates/java/') + '/templates'.length ); } else if (templatePath.includes('/templates/resources/')) { result = templatePath.substring( 0, templatePath.indexOf('/templates/resources/') + '/templates'.length ); } else if (templatePath.includes('/templates/src/')) { result = templatePath.substring( 0, templatePath.indexOf('/templates/src/') + '/templates'.length ); } else if (templatePath.includes('/templates/')) { result = templatePath.substring( 0, templatePath.indexOf('/templates/') + '/templates'.length ); } if (result.includes(path.sep)) { return result; } return result.split('/').join(path.sep); }; /** * @param {string} fileName * @returns {fileType} file type */ const getFileType = (fileName) => { // Normalize file path, if needed if (fileName.startsWith('/')) { fileName = fileName.substring(1); } if (fileName.startsWith('[[ --ModId-- ]]/')) { fileName = fileName.replace('[[ --ModId-- ]]/', ''); } // Try to get parent folder for easier detection const targetParentFolder = fileName.includes('/') ? fileName.split('/')[0] : ''; // Java Project files if (fileName.endsWith('.java') && !fileName.includes('src/main/java')) { return fileType.JAVA; } // Resources files if ( (fileName.endsWith('.json') || fileName.endsWith('.png') || fileName.endsWith('.fsh') || fileName.endsWith('.vsh') || fileName.endsWith('.mcmeta')) && !fileName.includes('src/main/resources/assets') && resourceLocations.indexOf(targetParentFolder) != -1 ) { return fileType.RESOURCE; } // Data files if ( (fileName.endsWith('.json') || fileName.endsWith('.nbt')) && !fileName.includes('src/main/resources/data') && dataLocations.indexOf(targetParentFolder) != -1 ) { return fileType.DATA; } // Data files (Minecraft) if ( fileName.startsWith('minecraft/') && (fileName.endsWith('.json') || fileName.endsWith('.nbt')) && !fileName.includes('src/main/resources/data') ) { if (dataLocations.indexOf(fileName.split('/')[1]) != -1) { return fileType.DATA_MINECRAFT; } } return fileType.UNKNOWN; }; export default { fileType, getBaseTemplatePath, getFileType, parse, read, };