Skip to content

Commit

Permalink
Use Gulp as a build system.
Browse files Browse the repository at this point in the history
  • Loading branch information
ajvincent committed Jan 2, 2025
1 parent 045f891 commit 97e9480
Show file tree
Hide file tree
Showing 56 changed files with 3,772 additions and 171 deletions.
38 changes: 36 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,41 @@
node_modules
playground

Gulpfile.d.ts
Gulpfile.js
Gulpfile.js.map

stage_*/*.js
stage_*/*.d.ts
stage_*/*.map
stage_*/**/*.js
stage_*/**/*.d.ts
stage_*/**/*.map
!stage_*/**/types/*.d.ts
!stage_*/**/types/**/*.d.ts
stage_*/tsconfig.json
stage_*/**/tsconfig.json

utilities/**/*.js
utilities/**/*.js.map
utilities/**/*.d.ts
!utilities_*/**/types/*.d.ts
!utilities_*/**/types/**/*.d.ts
utilities/tsconfig.json

use-cases/**/*.js
use-cases/**/*.js.map
use-cases/**/*.d.ts
!use-cases_*/**/types/*.d.ts
!use-cases_*/**/types/**/*.d.ts
use-cases/tsconfig.json

gulp-utilities/**/*.js
gulp-utilities/**/*.js.map
gulp-utilities/**/*.d.ts
!gulp-utilities/cleanTSC_output.js
!gulp-utilities/assertRepoIsClean.js

stage_2_generation/dist
stage_2_integration/snapshot
stage_2_integration/typings-snapshot
Expand All @@ -10,5 +46,3 @@ stage_3_generation/dist
stage_3_integration/snapshot
stage_3_integration/typings-snapshot
stage_3_snapshot/snapshot

.tsimp
14 changes: 14 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"files.exclude": {
"stage_*/**/*.js": { "when": "$(basename).ts"},
"stage_*/**/*.js.map": true,
"use-cases/**/*.js": { "when": "$(basename).ts"},
"use-cases/**/*.js.map": true,
"utilities/**/*.js": { "when": "$(basename).ts"},
"utilities/**/*.js.map": true,
"gulp-utilities/**/*.js": { "when": "$(basename).ts"},
"gulp-utilities/**/*.js.map": true,
"Gulpfile.js": true,
"Gulpfile.js.map": true,
}
}
93 changes: 93 additions & 0 deletions Gulpfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import path from "node:path";
import {
chdir,
cwd,
} from "node:process";
import {
fileURLToPath,
} from "node:url"

import {
series
} from "gulp";

import { InvokeTSC_main /*, InvokeTSC_prebuild */ } from "#gulp-utilities/InvokeTSC.js";

const projectRoot: string = path.normalize(path.dirname(
fileURLToPath(import.meta.url)
));

type VoidCallbackArray = (() => Promise<void>)[];

async function childGulpfile(
localPathToDir: string
): Promise<void>
{
const fullPathToDir = path.join(projectRoot, localPathToDir);

let stackDir: string;
function pushd(): Promise<void> {
stackDir = cwd();
chdir(path.normalize(path.join(stackDir, localPathToDir)));
return Promise.resolve();
}
pushd.displayName = `pushd(${localPathToDir})`;

function popd(): Promise<void> {
chdir(stackDir);
return Promise.resolve();
}
popd.displayName = `popd(${localPathToDir})`;

await pushd();
try {
await InvokeTSC_main();
const importCallbacks = (await import(path.join(fullPathToDir, "Gulpfile.js"))).default as VoidCallbackArray;
for (const callback of importCallbacks) {
await callback();
}
}
finally {
await popd();
}
}

function namedChildGulpFile(
localPathToDir: string
): ReturnType<typeof series>
{
const callback = () => childGulpfile(localPathToDir);
callback.displayName = localPathToDir;
return callback;
}

export const stage_one = series([
namedChildGulpFile("utilities"),
namedChildGulpFile("stage_0_references"),
namedChildGulpFile("stage_1_snapshot"),
]);

export const stage_two = series([
namedChildGulpFile("stage_2_generation"),
namedChildGulpFile("stage_2_integration/pre-build"),
namedChildGulpFile("stage_2_integration"),
namedChildGulpFile("stage_2_snapshot/pre-build"),
namedChildGulpFile("stage_2_snapshot"),
]);

export const stage_three = series([
namedChildGulpFile("stage_3_generation"),
namedChildGulpFile("stage_3_integration/pre-build"),
namedChildGulpFile("stage_3_integration"),
namedChildGulpFile("stage_3_snapshot/pre-build"),
namedChildGulpFile("stage_3_snapshot"),
]);

export const use_cases = namedChildGulpFile("use-cases");

export default series([
stage_one,
stage_two,
stage_three,
use_cases
]);
126 changes: 126 additions & 0 deletions gulp-utilities/InvokeTSC.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import fs from "node:fs/promises";
import path from "node:path";
import process from 'node:process';

import {
fileURLToPath,
} from "node:url";

import {
fork
} from "node:child_process";

const projectRoot: string = path.normalize(path.join(fileURLToPath(import.meta.url), "../.."));

const monorepoRoot: string = projectRoot;

//#region remove me
async function overwriteFileIfDifferent(
isContents: boolean,
sourceOrContents: string,
destination: string,
): Promise<boolean>
{
if (isContents === false) {
sourceOrContents = await fs.readFile(sourceOrContents, { encoding: "utf-8" });
}

let destStats: Awaited<ReturnType<typeof fs.stat>>;
let destContents: string | undefined, destFileFound = true;
try {
destStats = await fs.stat(destination);
destContents = await fs.readFile(destination, { encoding: "utf-8" });
}
catch (ex) {
void(ex);
destFileFound = false;
}

const contentsMatch = sourceOrContents === destContents;
if (destFileFound && contentsMatch) {
await fs.utimes(destination, destStats!.atime, destStats!.mtime)
return false;
}

await fs.writeFile(destination, sourceOrContents, { encoding: "utf-8" });
return true;
}

const TSC = path.resolve(monorepoRoot, "node_modules/typescript/bin/tsc");

async function InvokeTSC(
pathToBaseTSConfig: string,
excludesGlobs: string[],
): Promise<void>
{
const configContents = {
extends: pathToBaseTSConfig,
exclude: excludesGlobs,
}
if (excludesGlobs.length === 0) {
Reflect.deleteProperty(excludesGlobs, "excludes");
}

await overwriteFileIfDifferent(
true,
JSON.stringify(configContents, null, 2) + "\n",
path.join(process.cwd(), "tsconfig.json"),
);

const child = fork(TSC, [], {
cwd: process.cwd(),
stdio: ["ignore", "inherit", "inherit", "ipc"]
});

const p = new Promise<number | null>((resolve, reject) => {
child.on("exit", (code) => {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
code ? reject(code) : resolve(code);
});
});
try {
await p;
}
catch (code) {
throw new Error(`Failed on "${TSC}" with code ${code}`);
}
}

//#endregion remove me

export async function InvokeTSC_main(): Promise<void> {
const filesToExclude: string[] = [];
try {
const excludesJSON = await fs.readFile(path.join(process.cwd(), "tsc-excludes.json"), { encoding: "utf-8" });
filesToExclude.push(...JSON.parse(excludesJSON) as string[]);
}
catch (ex) {
void(ex);
}

return InvokeTSC(path.join(path.relative(process.cwd(), projectRoot), "tsconfig.json"), [
"./Gulpfile.ts",
"./pre-build/**",
"./generated/**",
...filesToExclude,
]);
}

export async function InvokeTSC_prebuild(): Promise<void> {
const curDir: string = process.cwd();
const buildDir: string = path.join(curDir, "pre-build");
try {
const dirStat = await fs.stat(buildDir);
if (dirStat.isDirectory() === false)
return;
} catch (ex) {
void(ex);
return;
}

await process.chdir(buildDir);
await InvokeTSC("../../tsconfig.json", [
"./Gulpfile.ts",
]);
await process.chdir(curDir);
}
28 changes: 28 additions & 0 deletions gulp-utilities/assertRepoIsClean.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* eslint-disable no-undef */
import which from "which";
import path from "node:path";
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import { fileURLToPath } from "node:url";

export const execAsync = promisify(execFile);

const projectDir = path.normalize(path.join(fileURLToPath(import.meta.url), "../.."));

const git = await which("git");

const child = await execAsync(
git,
["status", "-s"],
{ cwd: projectDir }
);

if (child.stderr) {
console.log(child.stderr);
}

if (child.stdout.trim()) {
console.log("repository is not clean:");
console.log(child.stdout);
process.exit(1);
}
44 changes: 44 additions & 0 deletions gulp-utilities/cleanTSC_output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import fs from "node:fs/promises";
import path from "node:path";
import {
fileURLToPath,
} from "node:url";

const projectRoot = path.normalize(path.join(fileURLToPath(import.meta.url), "../.."));

const dirEntries = await fs.readdir(
projectRoot,
{
encoding: "utf-8",
withFileTypes: true,
recursive: false,
}
);
const recursiveDirs = dirEntries.filter(dirEnt => dirEnt.isDirectory())
.map(dirEnt => dirEnt.name)
.filter(dirName => /^stage_\d+_/.test(dirName));
recursiveDirs.sort();
recursiveDirs.unshift("gulp-utilities", "utilities", "use-cases");

const TS_MODULE_EXT_RE = /(?<!\.d)\.ts$/;

const dirFilePromises = recursiveDirs.map(async recursiveDir => {
const dirPath = path.join(projectRoot, recursiveDir);
const descendants = await fs.readdir(dirPath, { "encoding": "utf-8", "recursive": true });
return descendants.map(d => path.join(projectRoot, recursiveDir, d));
});
dirFilePromises.unshift(
Promise.resolve(
dirEntries.filter(dirEnt => dirEnt.isFile()).map(dirEnt => path.join(projectRoot, dirEnt.name))
)
);

const allTSFiles = (await Promise.all(dirFilePromises)).flat().filter(f => TS_MODULE_EXT_RE.test(f));
const allCompiledFiles = allTSFiles.map(tsFile => [
tsFile.replace(/\.ts$/, ".js"),
tsFile.replace(/\.ts$/, ".d.ts"),
tsFile.replace(/\.ts$/, ".js.map")
]).flat();
allCompiledFiles.sort();

await Promise.all(allCompiledFiles.map(cf => fs.rm(cf, { force: true })));
Loading

0 comments on commit 97e9480

Please sign in to comment.