Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
# exclude files
debug-stick*.mcpack
debug-stick*.zip

dist/
*.mcpack
node_modules

pack/scripts/*
File renamed without changes.
File renamed without changes.
File renamed without changes
32 changes: 32 additions & 0 deletions build/gen_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import config from '../config.js';
import { execSync } from 'child_process';


const run = (cmd) => execSync(cmd).toString().trim();

const currCommit = run('git rev-parse HEAD');
const currBranch = run('git branch --show-current');

/**
* Stuff that will be used at runtime.
*/
export const runtimeConfigTable = {
version: config.packVersion,
minMcVer: config.minMcVersion,
apiVer: config.dependencies['@minecraft/server'],
uiApiVer: config.dependencies['@minecraft/server-ui'],
branch: currBranch,
commit: currCommit,
shCommit: currCommit.slice(0, 7),
};


/**
* Make a config script for the runtime config.
* @param cfg The generated runtime config object.
* @returns The config script.
*/
export function makeConfigScript(cfg) {
const jsonStr = JSON.stringify(cfg, null, 2);
return `export default ${jsonStr};`;
}
66 changes: 66 additions & 0 deletions build/gen_manifest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import config from '../config.js';
import { runtimeConfigTable as subTab } from './gen_config.js';
import { formatString } from './utils.js';


/**
* These stay constant.
*/
const PACK_UUID = '21aadfa6-e27c-400c-c596-596021852939';
const MODULE_DATA_UUID = 'd8a9ff21-7aa3-4b83-73ed-eeb141516e74';
const MODULE_SCRIPT_UUID = '86c7bab4-aed9-4297-5f0c-d5d62bd30be1';


/**
* Generate the manifest file from a template.
* @returns The generated manifest object.
*/
export function genManifest() {
return {
format_version: 3,

header: {
name: formatString(config.packName, subTab),
description: formatString(config.packDescription, subTab),
version: config.packVersion,
min_engine_version: config.minMcVersion,
uuid: PACK_UUID,
},

modules: [
{
description: 'behaviour',
type: 'data',
version: '1.0.0',
uuid: MODULE_DATA_UUID,
},
{
description: 'scripting',
type: 'script',
language: 'javascript',
version: '1.0.0',
uuid: MODULE_SCRIPT_UUID,
entry: config.scriptEntry,
}
],

dependencies: Object.entries(config.dependencies)
.map(([k, v]) => ({ module_name: k, version: v })),

metadata: {
authors: [ 'VYT' ],
license: 'MIT',
url: 'https://github.com/vytdev/debug-stick',
},
}
}


/**
* Pretty JSON-stringify the given manifest object.
* @param manifest The manifest object.
* @returns String.
*/
export function stringifyManifest(manifest) {
return JSON.stringify(manifest, null, 2);
}
41 changes: 41 additions & 0 deletions build/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { isAsyncFunction } from 'util/types';
import { cleanUp, compileSource, createDist, watchSource } from './tasks.js';


const actionTable = {};


/* --- HELP --- */
actionTable['help'] = () => console.error(
`usage: ${process.argv[1]} [task...]\n` +
'Utility script for working with the debug-stick project.\n' +
'Available tasks:\n' +
' help Shows this help\n' +
' pack Create dist package\n' +
' build Run: npx tsc --build\n' +
' watch Run: npx tsc --watch\n' +
' clean Remove generated files\n' +
'@vytdev'
);


actionTable['pack'] = createDist;
actionTable['build'] = compileSource;
actionTable['watch'] = watchSource;
actionTable['clean'] = cleanUp;


// Run each task in given order.
for (const task of process.argv.slice(2)) {
const fn = actionTable[task];
if (typeof fn !== 'function') {
console.error('task does not exist: ' + task);
continue;
}

// run the task synchronously
console.log(`--- ${task} ---`);
let code = isAsyncFunction(fn) ? await fn(task) : fn(task);
code = (code || 0) & 0xff;
if (code != 0) process.exit(code);
}
116 changes: 116 additions & 0 deletions build/tasks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import JSZip from 'jszip';
import fs from 'fs';
import fsp from 'fs/promises';
import path from 'path';

import config from '../config.js';
import { addDirToZip, formatString, runProcessAsync, writeZip } from './utils.js';
import { runtimeConfigTable, makeConfigScript } from './gen_config.js';
import { genManifest, stringifyManifest } from './gen_manifest.js';


/**
* Compile source code.
*/
export async function compileSource() {
console.log('compiling source ...');
const err = await runProcessAsync('npx', 'tsc', '--build');
console.log('typescript exited with code: ' + err);
}


/**
* Start live incremental compilation.
*/
export async function watchSource() {
console.log('watching src/ ...');
const err = await runProcessAsync('npx', 'tsc', '--watch');
console.log('stopped watching src/ !');
console.log('typescript exited with code: ' + err);
}


/**
* Add LICENSE file.
* @param zip
*/
export async function addLicense(zip) {
console.log('including LICENSE');
const str = fs.createReadStream('LICENSE');
zip.file('LICENSE', str)
}


/**
* Add scrips/config.js.
* @param zip
*/
export async function addConfig(zip) {
console.log('generating scripts/config.js');
const str = makeConfigScript(runtimeConfigTable);
zip.file('scripts/config.js', str);
}


/**
* Add manifest.json.
* @param zip
*/
export async function addManifest(zip) {
console.log('generating manifest.json');
const str = stringifyManifest(genManifest());
zip.file('manifest.json', str);
}


/**
* Add files from BP/ folder.
* @param zip
*/
export async function addBP(zip) {
return addDirToZip(zip, config.staticSrc);
}


/**
* Add files from dist/js-out/ to scripts/.
* @param zip
*/
export async function addJSOut(zip) {
const jsOut = path.join(config.distDir, 'js-out');
return addDirToZip(zip.folder('scripts'), jsOut);
}


/**
* Create dist package.
*/
export async function createDist() {
const zip = new JSZip();
await Promise.all([
addLicense(zip),
addConfig(zip),
addManifest(zip),
addBP(zip),
addJSOut(zip),
]);

if (!fs.statSync(config.distDir)?.isDirectory())
await fsp.mkdir(config.distDir);

const outFilePath = path.join(config.distDir,
formatString(config.outFileFmt, runtimeConfigTable));

await writeZip(zip, outFilePath);
}


/**
* Clean-up generated files. Note: does not include the dist files.
*/
export async function cleanUp() {
await fsp.rm(path.join(config.distDir, 'js-out'), {
recursive: true,
force: true,
});
}
85 changes: 85 additions & 0 deletions build/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import child_process from 'child_process';
import fs from 'fs';
import fsp from 'fs/promises';
import path from 'path';


/**
* Format string with '{key}' substitution.
* @param template The template, with optional '{key}'.
* @param subTab The substitution table.
* @returns The string.
*/
export function formatString(template, subTab) {
return template.replace(/\{([\w$_]+)\}/g, (old, arg) =>
(subTab[arg] ?? old));
}


/**
* Asynchronously run a sub-process.
* @param arg0 Name of or path to the executable.
* @param args Arguments to pass to the process.
* @returns A Promise.
*/
export async function runProcessAsync(arg0, ...args) {
return new Promise((resolve, reject) => {
const subproc = child_process.spawn(arg0, args, { stdio: 'inherit' });
// redirect sigint temporarily
const sigIntHandler = () => subproc.kill('SIGINT');
process.on('SIGINT', sigIntHandler);
// handle success and failure
subproc.on('close', code => {
process.off('SIGINT', sigIntHandler);
resolve(code || 0);
});
subproc.on('error', err => {
process.off('SIGINT', sigIntHandler);
reject(err)
});
});
}


/**
* Adds a directory into the given zip object.
* @param zipObj The zip object.
* @param folder The folder to zip.
*/
export async function addDirToZip(zipObj, folder) {
const items = await fsp.readdir(folder);

const addItem = async (item) => {
const fullPath = path.join(folder, item);
console.log('adding ' + fullPath); // feedback
const stat = await fsp.stat(fullPath);
if (stat.isDirectory()) {
const subFolder = zipObj.folder(item);
await addDirToZip(subFolder, fullPath);
} else {
zipObj.file(item, fs.createReadStream(fullPath));
}
};

await Promise.all(items.map(addItem));
}


/**
* Write the zip file.
* @param zipObj The zip object.
* @param outPath The output path.
*/
export async function writeZip(zipObj, outPath) {
const output = fs.createWriteStream(outPath);

zipObj.generateNodeStream({
type: 'nodebuffer',
streamFiles: true,
}).pipe(output);

return new Promise((resolve, reject) => {
output.on('finish', resolve);
output.on('error', reject);
});
}
Loading
Loading