186 lines
5.7 KiB
Java
186 lines
5.7 KiB
Java
|
import java.io.*;
|
||
|
import java.nio.charset.*;
|
||
|
import java.time.*;
|
||
|
import java.util.*;
|
||
|
import java.util.zip.*;
|
||
|
|
||
|
// Web application asset packager
|
||
|
public class Bundle {
|
||
|
|
||
|
// Read a file from disk into a byte buffer
|
||
|
static byte[] fileRead(File file) {
|
||
|
try (var stream = new FileInputStream(file)) {
|
||
|
return stream.readAllBytes();
|
||
|
} catch (Exception e) { return null; }
|
||
|
}
|
||
|
|
||
|
// Retrieve the canonical form of a file
|
||
|
static File getFile(File file) {
|
||
|
try { return file.getCanonicalFile(); }
|
||
|
catch (Exception e) { return null; }
|
||
|
}
|
||
|
|
||
|
// Retrieve the canonical file for a relative filename
|
||
|
static File getFile(String filename) {
|
||
|
return getFile(new File(filename));
|
||
|
}
|
||
|
|
||
|
// List all canonical files within a directory
|
||
|
static File[] listFiles(File dir) {
|
||
|
var ret = dir.listFiles();
|
||
|
for (int x = 0; x < ret.length; x++)
|
||
|
ret[x] = getFile(ret[x]);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
// List all asset files to be bundled
|
||
|
static Asset[] listAssets(File root, File main) {
|
||
|
var assets = new ArrayList<Asset>();
|
||
|
var dirs = new ArrayList<File >();
|
||
|
|
||
|
// Initial directories
|
||
|
dirs.add(new File(root, "shrooms-vb-core"));
|
||
|
dirs.add(new File(root, "shrooms-vb-web" ));
|
||
|
|
||
|
// Process all directories
|
||
|
while (dirs.size() != 0) {
|
||
|
var dir = dirs.remove(0);
|
||
|
|
||
|
// Process all child files and directories
|
||
|
for (var file : listFiles(dir)) {
|
||
|
|
||
|
// Exclude this file or directory
|
||
|
if (file.equals(main) || file.getName().startsWith(".git"))
|
||
|
continue;
|
||
|
|
||
|
// Include this directory
|
||
|
if (file.isDirectory())
|
||
|
dirs.add(file);
|
||
|
|
||
|
// Include this file
|
||
|
else assets.add(new Asset(root, file));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Collections.sort(assets);
|
||
|
return assets.toArray(new Asset[assets.size()]);
|
||
|
}
|
||
|
|
||
|
// Determine the relative path of a file from the root directory
|
||
|
static String relativePath(File root, File file) {
|
||
|
|
||
|
// Work backwards to identify the full path
|
||
|
var path = new ArrayList<String>();
|
||
|
while (!root.equals(file)) {
|
||
|
path.add(0, file.getName());
|
||
|
file = file.getParentFile();
|
||
|
}
|
||
|
|
||
|
// Join the path parts with forward slashes
|
||
|
var ret = new StringBuilder();
|
||
|
for (String part : path)
|
||
|
ret.append("/" + part);
|
||
|
return ret.toString().substring(1);
|
||
|
}
|
||
|
|
||
|
// Express a byte array as a Base64 string
|
||
|
static String toBase64(byte[] data) {
|
||
|
return Base64.getMimeEncoder(0, new byte[0]).encodeToString(data);
|
||
|
}
|
||
|
|
||
|
// Encode a byte array as a zlib buffer
|
||
|
static byte[] toZlib(byte[] data) {
|
||
|
try {
|
||
|
var comp = new Deflater(Deflater.BEST_COMPRESSION, false);
|
||
|
comp.setInput(data);
|
||
|
comp.finish();
|
||
|
var ret = new byte[data.length];
|
||
|
ret = Arrays.copyOf(ret, comp.deflate(ret));
|
||
|
comp.end();
|
||
|
return ret;
|
||
|
} catch (Exception e) { throw new RuntimeException(e.getMessage()); }
|
||
|
}
|
||
|
|
||
|
// Program entry point
|
||
|
public static void main(String[] args) {
|
||
|
|
||
|
// Select all assets
|
||
|
var root = getFile("../");
|
||
|
var main = getFile("main.js");
|
||
|
var assets = listAssets(root, main);
|
||
|
|
||
|
// Resolve the current date code
|
||
|
var today = ZonedDateTime.now(Clock.systemUTC());
|
||
|
String dateCode = String.format("%04d%02d%02d",
|
||
|
today.getYear(), today.getMonthValue(), today.getDayOfMonth());
|
||
|
|
||
|
// Process the manifest
|
||
|
var manifest = new StringBuilder();
|
||
|
manifest.append("[");
|
||
|
for (var asset : assets) {
|
||
|
manifest.append(String.format("%s\"%s\",%d",
|
||
|
asset == assets[0] ? "" : ",", asset.name, asset.length));
|
||
|
}
|
||
|
manifest.append(",\"" + dateCode + "\"]");
|
||
|
|
||
|
// Encode the bundle
|
||
|
var bundle = new ByteArrayOutputStream();
|
||
|
try {
|
||
|
bundle.write(fileRead(main));
|
||
|
bundle.write(0);
|
||
|
bundle.write(manifest.toString().getBytes(StandardCharsets.UTF_8));
|
||
|
bundle.write(0);
|
||
|
for (var asset : assets)
|
||
|
bundle.write(fileRead(asset.file));
|
||
|
} catch (Exception e) {}
|
||
|
|
||
|
// Read the HTML template
|
||
|
var template = new String(
|
||
|
fileRead(new File("template.html")), StandardCharsets.UTF_8)
|
||
|
.split("\\\"\\\"");
|
||
|
|
||
|
// Generate the output HTML file
|
||
|
String filename = "../acid-shroom_" + dateCode + ".html";
|
||
|
try (var stream = new FileOutputStream(filename)) {
|
||
|
stream.write(template[0].getBytes(StandardCharsets.UTF_8));
|
||
|
stream.write('"');
|
||
|
stream.write(toBase64(toZlib(bundle.toByteArray()))
|
||
|
.getBytes(StandardCharsets.UTF_8));
|
||
|
stream.write('"');
|
||
|
stream.write(template[1].getBytes(StandardCharsets.UTF_8));
|
||
|
} catch (Exception e) {}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
///////////////////////////////// Classes /////////////////////////////////
|
||
|
|
||
|
// Packaged asset file
|
||
|
static class Asset implements Comparable<Asset> {
|
||
|
File file; // File on disk
|
||
|
int length; // Size in bytes
|
||
|
String name; // Filename without path
|
||
|
|
||
|
Asset(File root, File file) {
|
||
|
this.file = file;
|
||
|
length = (int) file.length();
|
||
|
name = relativePath(root, file);
|
||
|
}
|
||
|
|
||
|
public int compareTo(Asset o) {
|
||
|
return name.compareTo(o.name);
|
||
|
}
|
||
|
|
||
|
public boolean equals(Object o) {
|
||
|
return o instanceof Asset && compareTo((Asset) o) == 0;
|
||
|
}
|
||
|
|
||
|
public int hashCode() {
|
||
|
return name.hashCode();
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|