format gulpfile

This commit is contained in:
Johannes Loher 2021-01-06 01:31:23 +01:00
parent c01a2eb29d
commit b4f697f9cc

View file

@ -1,429 +1,429 @@
const gulp = require("gulp"); const gulp = require("gulp");
const fs = require("fs-extra"); const fs = require("fs-extra");
const path = require("path"); const path = require("path");
const chalk = require("chalk"); const chalk = require("chalk");
const archiver = require("archiver"); const archiver = require("archiver");
const stringify = require("json-stringify-pretty-compact"); const stringify = require("json-stringify-pretty-compact");
const typescript = require("typescript"); const typescript = require("typescript");
const ts = require("gulp-typescript"); const ts = require("gulp-typescript");
const less = require("gulp-less"); const less = require("gulp-less");
const sass = require("gulp-sass"); const sass = require("gulp-sass");
const git = require("gulp-git"); const git = require("gulp-git");
const argv = require("yargs").argv; const argv = require("yargs").argv;
sass.compiler = require("sass"); sass.compiler = require("sass");
function getConfig() { function getConfig() {
const configPath = path.resolve(process.cwd(), "foundryconfig.json"); const configPath = path.resolve(process.cwd(), "foundryconfig.json");
let config; let config;
if (fs.existsSync(configPath)) { if (fs.existsSync(configPath)) {
config = fs.readJSONSync(configPath); config = fs.readJSONSync(configPath);
return config; return config;
} else { } else {
return; return;
} }
} }
function getManifest() { function getManifest() {
const json = {}; const json = {};
if (fs.existsSync("src")) { if (fs.existsSync("src")) {
json.root = "src"; json.root = "src";
} else { } else {
json.root = "dist"; json.root = "dist";
} }
const modulePath = path.join(json.root, "module.json"); const modulePath = path.join(json.root, "module.json");
const systemPath = path.join(json.root, "system.json"); const systemPath = path.join(json.root, "system.json");
if (fs.existsSync(modulePath)) { if (fs.existsSync(modulePath)) {
json.file = fs.readJSONSync(modulePath); json.file = fs.readJSONSync(modulePath);
json.name = "module.json"; json.name = "module.json";
} else if (fs.existsSync(systemPath)) { } else if (fs.existsSync(systemPath)) {
json.file = fs.readJSONSync(systemPath); json.file = fs.readJSONSync(systemPath);
json.name = "system.json"; json.name = "system.json";
} else { } else {
return; return;
} }
return json; return json;
} }
/** /**
* TypeScript transformers * TypeScript transformers
* @returns {typescript.TransformerFactory<typescript.SourceFile>} * @returns {typescript.TransformerFactory<typescript.SourceFile>}
*/ */
function createTransformer() { function createTransformer() {
/** /**
* @param {typescript.Node} node * @param {typescript.Node} node
*/ */
function shouldMutateModuleSpecifier(node) { function shouldMutateModuleSpecifier(node) {
if (!typescript.isImportDeclaration(node) && !typescript.isExportDeclaration(node)) return false; if (!typescript.isImportDeclaration(node) && !typescript.isExportDeclaration(node)) return false;
if (node.moduleSpecifier === undefined) return false; if (node.moduleSpecifier === undefined) return false;
if (!typescript.isStringLiteral(node.moduleSpecifier)) return false; if (!typescript.isStringLiteral(node.moduleSpecifier)) return false;
if (!node.moduleSpecifier.text.startsWith("./") && !node.moduleSpecifier.text.startsWith("../")) return false; if (!node.moduleSpecifier.text.startsWith("./") && !node.moduleSpecifier.text.startsWith("../")) return false;
if (path.extname(node.moduleSpecifier.text) !== "") return false; if (path.extname(node.moduleSpecifier.text) !== "") return false;
return true; return true;
} }
/** /**
* Transforms import/export declarations to append `.js` extension * Transforms import/export declarations to append `.js` extension
* @param {typescript.TransformationContext} context * @param {typescript.TransformationContext} context
*/ */
function importTransformer(context) { function importTransformer(context) {
return (node) => { return (node) => {
/** /**
* @param {typescript.Node} node * @param {typescript.Node} node
*/ */
function visitor(node) { function visitor(node) {
if (shouldMutateModuleSpecifier(node)) { if (shouldMutateModuleSpecifier(node)) {
if (typescript.isImportDeclaration(node)) { if (typescript.isImportDeclaration(node)) {
const newModuleSpecifier = typescript.createLiteral(`${node.moduleSpecifier.text}.js`); const newModuleSpecifier = typescript.createLiteral(`${node.moduleSpecifier.text}.js`);
return typescript.updateImportDeclaration( return typescript.updateImportDeclaration(
node, node,
node.decorators, node.decorators,
node.modifiers, node.modifiers,
node.importClause, node.importClause,
newModuleSpecifier newModuleSpecifier,
); );
} else if (typescript.isExportDeclaration(node)) { } else if (typescript.isExportDeclaration(node)) {
const newModuleSpecifier = typescript.createLiteral(`${node.moduleSpecifier.text}.js`); const newModuleSpecifier = typescript.createLiteral(`${node.moduleSpecifier.text}.js`);
return typescript.updateExportDeclaration( return typescript.updateExportDeclaration(
node, node,
node.decorators, node.decorators,
node.modifiers, node.modifiers,
node.exportClause, node.exportClause,
newModuleSpecifier newModuleSpecifier,
); );
} }
} }
return typescript.visitEachChild(node, visitor, context); return typescript.visitEachChild(node, visitor, context);
} }
return typescript.visitNode(node, visitor); return typescript.visitNode(node, visitor);
}; };
} }
return importTransformer; return importTransformer;
} }
const tsConfig = ts.createProject("tsconfig.json", { const tsConfig = ts.createProject("tsconfig.json", {
getCustomTransformers: (_program) => ({ getCustomTransformers: (_program) => ({
after: [createTransformer()], after: [createTransformer()],
}), }),
}); });
/********************/ /********************/
/* BUILD */ /* BUILD */
/********************/ /********************/
/** /**
* Build TypeScript * Build TypeScript
*/ */
function buildTS() { function buildTS() {
return gulp.src("src/**/*.ts").pipe(tsConfig()).pipe(gulp.dest("dist")); return gulp.src("src/**/*.ts").pipe(tsConfig()).pipe(gulp.dest("dist"));
} }
/** /**
* Build Less * Build Less
*/ */
function buildLess() { function buildLess() {
return gulp.src("src/*.less").pipe(less()).pipe(gulp.dest("dist")); return gulp.src("src/*.less").pipe(less()).pipe(gulp.dest("dist"));
} }
/** /**
* Build SASS * Build SASS
*/ */
function buildSASS() { function buildSASS() {
return gulp.src("src/*.scss").pipe(sass().on("error", sass.logError)).pipe(gulp.dest("dist")); return gulp.src("src/*.scss").pipe(sass().on("error", sass.logError)).pipe(gulp.dest("dist"));
} }
/** /**
* Copy static files * Copy static files
*/ */
async function copyFiles() { async function copyFiles() {
const statics = ["lang", "fonts", "assets", "templates", "module.json", "system.json", "template.json"]; const statics = ["lang", "fonts", "assets", "templates", "module.json", "system.json", "template.json"];
try { try {
for (const file of statics) { for (const file of statics) {
if (fs.existsSync(path.join("src", file))) { if (fs.existsSync(path.join("src", file))) {
await fs.copy(path.join("src", file), path.join("dist", file)); await fs.copy(path.join("src", file), path.join("dist", file));
} }
} }
return Promise.resolve(); return Promise.resolve();
} catch (err) { } catch (err) {
Promise.reject(err); Promise.reject(err);
} }
} }
/** /**
* Watch for changes for each build step * Watch for changes for each build step
*/ */
function buildWatch() { function buildWatch() {
gulp.watch("src/**/*.ts", { ignoreInitial: false }, buildTS); gulp.watch("src/**/*.ts", { ignoreInitial: false }, buildTS);
gulp.watch("src/**/*.less", { ignoreInitial: false }, buildLess); gulp.watch("src/**/*.less", { ignoreInitial: false }, buildLess);
gulp.watch("src/**/*.scss", { ignoreInitial: false }, buildSASS); gulp.watch("src/**/*.scss", { ignoreInitial: false }, buildSASS);
gulp.watch(["src/fonts", "src/lang", "src/templates", "src/*.json"], { ignoreInitial: false }, copyFiles); gulp.watch(["src/fonts", "src/lang", "src/templates", "src/*.json"], { ignoreInitial: false }, copyFiles);
} }
/********************/ /********************/
/* CLEAN */ /* CLEAN */
/********************/ /********************/
/** /**
* Remove built files from `dist` folder * Remove built files from `dist` folder
* while ignoring source files * while ignoring source files
*/ */
async function clean() { async function clean() {
const name = path.basename(path.resolve(".")); const name = path.basename(path.resolve("."));
const files = []; const files = [];
// If the project uses TypeScript // If the project uses TypeScript
if (fs.existsSync(path.join("src", `${name}.ts`))) { if (fs.existsSync(path.join("src", `${name}.ts`))) {
files.push( files.push(
"lang", "lang",
"templates", "templates",
"assets", "assets",
"module", "module",
`${name}.js`, `${name}.js`,
"module.json", "module.json",
"system.json", "system.json",
"template.json" "template.json",
); );
} }
// If the project uses Less or SASS // If the project uses Less or SASS
if (fs.existsSync(path.join("src", `${name}.less`)) || fs.existsSync(path.join("src", `${name}.scss`))) { if (fs.existsSync(path.join("src", `${name}.less`)) || fs.existsSync(path.join("src", `${name}.scss`))) {
files.push("fonts", `${name}.css`); files.push("fonts", `${name}.css`);
} }
console.log(" ", chalk.yellow("Files to clean:")); console.log(" ", chalk.yellow("Files to clean:"));
console.log(" ", chalk.blueBright(files.join("\n "))); console.log(" ", chalk.blueBright(files.join("\n ")));
// Attempt to remove the files // Attempt to remove the files
try { try {
for (const filePath of files) { for (const filePath of files) {
await fs.remove(path.join("dist", filePath)); await fs.remove(path.join("dist", filePath));
} }
return Promise.resolve(); return Promise.resolve();
} catch (err) { } catch (err) {
Promise.reject(err); Promise.reject(err);
} }
} }
/********************/ /********************/
/* LINK */ /* LINK */
/********************/ /********************/
/** /**
* Link build to User Data folder * Link build to User Data folder
*/ */
async function linkUserData() { async function linkUserData() {
const name = path.basename(path.resolve(".")); const name = path.basename(path.resolve("."));
const config = fs.readJSONSync("foundryconfig.json"); const config = fs.readJSONSync("foundryconfig.json");
let destDir; let destDir;
try { try {
if ( if (
fs.existsSync(path.resolve(".", "dist", "module.json")) || fs.existsSync(path.resolve(".", "dist", "module.json")) ||
fs.existsSync(path.resolve(".", "src", "module.json")) fs.existsSync(path.resolve(".", "src", "module.json"))
) { ) {
destDir = "modules"; destDir = "modules";
} else if ( } else if (
fs.existsSync(path.resolve(".", "dist", "system.json")) || fs.existsSync(path.resolve(".", "dist", "system.json")) ||
fs.existsSync(path.resolve(".", "src", "system.json")) fs.existsSync(path.resolve(".", "src", "system.json"))
) { ) {
destDir = "systems"; destDir = "systems";
} else { } else {
throw Error(`Could not find ${chalk.blueBright("module.json")} or ${chalk.blueBright("system.json")}`); throw Error(`Could not find ${chalk.blueBright("module.json")} or ${chalk.blueBright("system.json")}`);
} }
let linkDir; let linkDir;
if (config.dataPath) { if (config.dataPath) {
if (!fs.existsSync(path.join(config.dataPath, "Data"))) if (!fs.existsSync(path.join(config.dataPath, "Data")))
throw Error("User Data path invalid, no Data directory found"); throw Error("User Data path invalid, no Data directory found");
linkDir = path.join(config.dataPath, "Data", destDir, name); linkDir = path.join(config.dataPath, "Data", destDir, name);
} else { } else {
throw Error("No User Data path defined in foundryconfig.json"); throw Error("No User Data path defined in foundryconfig.json");
} }
if (argv.clean || argv.c) { if (argv.clean || argv.c) {
console.log(chalk.yellow(`Removing build in ${chalk.blueBright(linkDir)}`)); console.log(chalk.yellow(`Removing build in ${chalk.blueBright(linkDir)}`));
await fs.remove(linkDir); await fs.remove(linkDir);
} else if (!fs.existsSync(linkDir)) { } else if (!fs.existsSync(linkDir)) {
console.log(chalk.green(`Copying build to ${chalk.blueBright(linkDir)}`)); console.log(chalk.green(`Copying build to ${chalk.blueBright(linkDir)}`));
await fs.symlink(path.resolve("./dist"), linkDir); await fs.symlink(path.resolve("./dist"), linkDir);
} }
return Promise.resolve(); return Promise.resolve();
} catch (err) { } catch (err) {
Promise.reject(err); Promise.reject(err);
} }
} }
/*********************/ /*********************/
/* PACKAGE */ /* PACKAGE */
/*********************/ /*********************/
/** /**
* Package build * Package build
*/ */
async function packageBuild() { async function packageBuild() {
const manifest = getManifest(); const manifest = getManifest();
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
try { try {
// Remove the package dir without doing anything else // Remove the package dir without doing anything else
if (argv.clean || argv.c) { if (argv.clean || argv.c) {
console.log(chalk.yellow("Removing all packaged files")); console.log(chalk.yellow("Removing all packaged files"));
fs.removeSync("package"); fs.removeSync("package");
return; return;
} }
// Ensure there is a directory to hold all the packaged versions // Ensure there is a directory to hold all the packaged versions
fs.ensureDirSync("package"); fs.ensureDirSync("package");
// Initialize the zip file // Initialize the zip file
const zipName = `${manifest.file.name}-v${manifest.file.version}.zip`; const zipName = `${manifest.file.name}-v${manifest.file.version}.zip`;
const zipFile = fs.createWriteStream(path.join("package", zipName)); const zipFile = fs.createWriteStream(path.join("package", zipName));
const zip = archiver("zip", { zlib: { level: 9 } }); const zip = archiver("zip", { zlib: { level: 9 } });
zipFile.on("close", () => { zipFile.on("close", () => {
console.log(chalk.green(zip.pointer() + " total bytes")); console.log(chalk.green(zip.pointer() + " total bytes"));
console.log(chalk.green(`Zip file ${zipName} has been written`)); console.log(chalk.green(`Zip file ${zipName} has been written`));
return resolve(); return resolve();
}); });
zip.on("error", (err) => { zip.on("error", (err) => {
throw err; throw err;
}); });
zip.pipe(zipFile); zip.pipe(zipFile);
// Add the directory with the final code // Add the directory with the final code
zip.directory("dist/", manifest.file.name); zip.directory("dist/", manifest.file.name);
zip.finalize(); zip.finalize();
} catch (err) { } catch (err) {
return reject(err); return reject(err);
} }
}); });
} }
/*********************/ /*********************/
/* PACKAGE */ /* PACKAGE */
/*********************/ /*********************/
/** /**
* Update version and URLs in the manifest JSON * Update version and URLs in the manifest JSON
*/ */
function updateManifest(cb) { function updateManifest(cb) {
const packageJson = fs.readJSONSync("package.json"); const packageJson = fs.readJSONSync("package.json");
const config = getConfig(), const config = getConfig(),
manifest = getManifest(), manifest = getManifest(),
rawURL = config.rawURL, rawURL = config.rawURL,
repoURL = config.repository, repoURL = config.repository,
manifestRoot = manifest.root; manifestRoot = manifest.root;
if (!config) cb(Error(chalk.red("foundryconfig.json not found"))); if (!config) cb(Error(chalk.red("foundryconfig.json not found")));
if (!manifest) cb(Error(chalk.red("Manifest JSON not found"))); if (!manifest) cb(Error(chalk.red("Manifest JSON not found")));
if (!rawURL || !repoURL) cb(Error(chalk.red("Repository URLs not configured in foundryconfig.json"))); if (!rawURL || !repoURL) cb(Error(chalk.red("Repository URLs not configured in foundryconfig.json")));
try { try {
const version = argv.update || argv.u; const version = argv.update || argv.u;
/* Update version */ /* Update version */
const versionMatch = /^(\d{1,}).(\d{1,}).(\d{1,})$/; const versionMatch = /^(\d{1,}).(\d{1,}).(\d{1,})$/;
const currentVersion = manifest.file.version; const currentVersion = manifest.file.version;
let targetVersion = ""; let targetVersion = "";
if (!version) { if (!version) {
cb(Error("Missing version number")); cb(Error("Missing version number"));
} }
if (versionMatch.test(version)) { if (versionMatch.test(version)) {
targetVersion = version; targetVersion = version;
} else { } else {
targetVersion = currentVersion.replace(versionMatch, (substring, major, minor, patch) => { targetVersion = currentVersion.replace(versionMatch, (substring, major, minor, patch) => {
console.log(substring, Number(major) + 1, Number(minor) + 1, Number(patch) + 1); console.log(substring, Number(major) + 1, Number(minor) + 1, Number(patch) + 1);
if (version === "major") { if (version === "major") {
return `${Number(major) + 1}.0.0`; return `${Number(major) + 1}.0.0`;
} else if (version === "minor") { } else if (version === "minor") {
return `${major}.${Number(minor) + 1}.0`; return `${major}.${Number(minor) + 1}.0`;
} else if (version === "patch") { } else if (version === "patch") {
return `${major}.${minor}.${Number(patch) + 1}`; return `${major}.${minor}.${Number(patch) + 1}`;
} else { } else {
return ""; return "";
} }
}); });
} }
if (targetVersion === "") { if (targetVersion === "") {
return cb(Error(chalk.red("Error: Incorrect version arguments."))); return cb(Error(chalk.red("Error: Incorrect version arguments.")));
} }
if (targetVersion === currentVersion) { if (targetVersion === currentVersion) {
return cb(Error(chalk.red("Error: Target version is identical to current version."))); return cb(Error(chalk.red("Error: Target version is identical to current version.")));
} }
console.log(`Updating version number to '${targetVersion}'`); console.log(`Updating version number to '${targetVersion}'`);
packageJson.version = targetVersion; packageJson.version = targetVersion;
manifest.file.version = targetVersion; manifest.file.version = targetVersion;
/* Update URLs */ /* Update URLs */
const result = `${rawURL}/v${manifest.file.version}/package/${manifest.file.name}-v${manifest.file.version}.zip`; const result = `${rawURL}/v${manifest.file.version}/package/${manifest.file.name}-v${manifest.file.version}.zip`;
manifest.file.url = repoURL; manifest.file.url = repoURL;
manifest.file.manifest = `${rawURL}/master/${manifestRoot}/${manifest.name}`; manifest.file.manifest = `${rawURL}/master/${manifestRoot}/${manifest.name}`;
manifest.file.download = result; manifest.file.download = result;
const prettyProjectJson = stringify(manifest.file, { const prettyProjectJson = stringify(manifest.file, {
maxLength: 35, maxLength: 35,
indent: "\t", indent: "\t",
}); });
fs.writeJSONSync("package.json", packageJson, { spaces: "\t" }); fs.writeJSONSync("package.json", packageJson, { spaces: "\t" });
fs.writeFileSync(path.join(manifest.root, manifest.name), prettyProjectJson, "utf8"); fs.writeFileSync(path.join(manifest.root, manifest.name), prettyProjectJson, "utf8");
return cb(); return cb();
} catch (err) { } catch (err) {
cb(err); cb(err);
} }
} }
function gitAdd() { function gitAdd() {
return gulp.src("package").pipe(git.add({ args: "--no-all" })); return gulp.src("package").pipe(git.add({ args: "--no-all" }));
} }
function gitCommit() { function gitCommit() {
return gulp.src("./*").pipe( return gulp.src("./*").pipe(
git.commit(`v${getManifest().file.version}`, { git.commit(`v${getManifest().file.version}`, {
args: "-a", args: "-a",
disableAppendPaths: true, disableAppendPaths: true,
}) }),
); );
} }
function gitTag() { function gitTag() {
const manifest = getManifest(); const manifest = getManifest();
return git.tag(`v${manifest.file.version}`, `Updated to ${manifest.file.version}`, (err) => { return git.tag(`v${manifest.file.version}`, `Updated to ${manifest.file.version}`, (err) => {
if (err) throw err; if (err) throw err;
}); });
} }
const execGit = gulp.series(gitAdd, gitCommit, gitTag); const execGit = gulp.series(gitAdd, gitCommit, gitTag);
const execBuild = gulp.parallel(buildTS, buildLess, buildSASS, copyFiles); const execBuild = gulp.parallel(buildTS, buildLess, buildSASS, copyFiles);
exports.build = gulp.series(clean, execBuild); exports.build = gulp.series(clean, execBuild);
exports.watch = buildWatch; exports.watch = buildWatch;
exports.clean = clean; exports.clean = clean;
exports.link = linkUserData; exports.link = linkUserData;
exports.package = packageBuild; exports.package = packageBuild;
exports.update = updateManifest; exports.update = updateManifest;
exports.publish = gulp.series(clean, updateManifest, execBuild, packageBuild, execGit); exports.publish = gulp.series(clean, updateManifest, execBuild, packageBuild, execGit);