refactor: update build environment

refactored to stick to my UNIX principles, in addition to exposing a cleaner GNU
Make interface
This commit is contained in:
Rodney, Tiara 2025-04-25 18:43:19 +02:00
parent 219f877888
commit fcec0ee5b1
No known key found for this signature in database
GPG key ID: 5CD8EC1D46106723
5 changed files with 1061 additions and 3520 deletions

0
LICENSE Normal file
View file

View file

@ -1,28 +1,40 @@
NPM=npm
.PHONY: configure chore build dist test publish doc
.PHONY: configure chore package-lock.json publish dist
all: chore
all: dist
chore: configure
tags:
ctags -R --exclude=node_modules --exclude=vendor --exclude=docs \
--exclude=*.js --exclude=*.htm* --exclude=*.json --exclude=src/style
chore: configure package-lock.json
configure:
autoconf
build:
$(NPM) run build
build/release/: node_modules/ src/ tsconfig.json
$(NPM) run build:release
test:
$(NPM) run test
build/debug/: node_modules/ src/ tsconfig.debug.json
$(NPM) run build:debug
doc:
build/doc/: node_modules/ src/ typedoc.json tsconfig.json
$(NPM) run doc
dist:
$(NPM) run dist
test-reports/: node_modules/ tests/ src/ jest.config.mjs
$(NPM) run test
publish:
$(NPM) run mypublish -- -r '$(NPM_REGISTRY)'
dist: build/release/ build/doc/
$(NPM) run dist -- build/doc/
publish: node_modules/ tsconfig.node.json
$(NPM) run publish_ -- -r '$(NPM_REGISTRY)'
# necessary when using a local npm mirror/proxy
package-lock.json: package.json
rm -rf package-lock.json
npm install --registry=https://registry.npmjs.org
clean:
rm -rvf configure~ config.log config.status autom4te.cache build dist

4267
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
"name": "administratrix/esm-logging",
"version": "1.0.0",
"description": "port of Python standard library logging module",
"main": "index.js",
"main": "lib/index.js",
"scripts": {
"test": "jest",
"build": "npm run build:release",
@ -12,7 +12,7 @@
"watch:debug": "npm run build:debug -- --watch",
"doc": "typedoc --entryPoints src/index.ts --html build/doc",
"mypublish": "ts-node -P tsconfig.node.json scripts/publish.ts",
"dist": "ts-node -P tsconfig.node.json scripts/pack.ts build/release dist"
"dist": "ts-node -P tsconfig.node.json scripts/npm-pack.ts build/release dist"
},
"repository": {
"type": "git",
@ -24,6 +24,7 @@
"@types/jest": "^29.5.14",
"jest": "^29.7.0",
"ts-jest": "^29.3.2",
"ts-node": "^10.9.2",
"typedoc": "^0.27.9",
"typescript": "^5.8.3"
}

273
scripts/npm-pack.ts Normal file
View file

@ -0,0 +1,273 @@
import * as child_process from 'node:child_process';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import * as util from 'node:util';
const SCRIPTNAME = path.basename(__filename);
const DEFAULT_INPUT_DIR: string = path.join('build', 'release');
const DEFAULT_OUTPUT_DIR: string = 'dist';
const DEFAULT_ASSETS_INDEX_BASENAME: string = 'assets.txt';
const DEFAULT_DOCS_DIRNAME: string = 'docs/';
const DEFAULT_LIB_DIRNAME: string = 'lib/';
function usage(exec: string): string {
return `
Usage: ${exec} [OPTIONS] [INPUT] [OUTPUT] [DOCS]
Create a tarball from a package
This script is a wrapper around \`npm pack\`.
This project uses npm pack to distribute its assets, even though it's not the
most ideal as the resulting tarball has a directory structure specific to npm
packages. The reason it is still utilized though is because it does not require
any additional dependencies outside the Node.js/npm runtime environment.
It copies the current working directory to a temporary directory, moves the
build artifacts to the root of it and then starts \`npm pack\` from within that
directory.
This is necessary, because traditional directory layout of build environments
isn't compatible with npm pack routine. With npm packages, usually build
artifacts are transpiled into the same directory as their sources and then
excluded via e.g. \`.gitignore\`. This is how the \`npm pack\` routine expects
it, which gives poor isolation between sources and build artifacts.
Positional arguments:
INPUT - directory of build output used as the input for packaging
[default:${DEFAULT_INPUT_DIR}]
OUTPUT - directory to output the package (tarball) into
[default:${DEFAULT_OUTPUT_DIR}]
DOCS - directory containing documentation, which is used as an auxiliary
input for packaging alongside INPUT.
Options:
-d, --docs-dirname - name of directory to output documentation into
(if supplied through DOCS).
[default:${DEFAULT_DOCS_DIRNAME}]
-l, --lib-dirname - name of directory to output transpiled module into
[default:${DEFAULT_LIB_DIRNAME}]
`;
}
export interface PackageOptions {
inputDir: string,
outputDir: string,
docsInputDir: string | null,
docsOutputDirname: string,
libOutputDirname: string,
}
/**
* recursively traverse a directory and yield all paths relative to the root
*
* @param root - path where to start traversal from
*/
export function* listFiles(
root: string,
trueRoot?: string,
): Generator<string, void, unknown> {
trueRoot = trueRoot ?? root;
if (!fs.existsSync(root)) {
throw new Error(`path does not exist: '${root}'`);
}
var files = fs.readdirSync(root);
for (var i = 0; i < files.length; i++) {
let filePath = path.join(root, files[i]);
let stat = fs.lstatSync(filePath);
if (stat.isDirectory()) { yield* listFiles(filePath, trueRoot) }
else { yield path.relative(trueRoot, filePath); }
};
};
/**
* TODO: write TSDOC block comment
*/
function pack(options: PackageOptions): void {
var cwd = process.cwd();
if (path.dirname(options.inputDir) == path.basename(options.inputDir)) {
throw new Error('inputDir must have a nesting depth of at least 2')
}
if ([path.sep, '.'].includes(options.inputDir[0])) {
throw new Error(`inputDir must be a relative path inside of '${cwd}'`)
}
console.log(`${SCRIPTNAME}: creating temporary directory...`);
const tempDir = fs.mkdtempSync(path.join(
os.tmpdir(),
`${path.basename(cwd)}-`
));
console.log(`${SCRIPTNAME}: copying metadata...`);
[
'package.json',
'LICENSE',
'README.md',
].forEach((target) => {
// TODO: sync file stats
fs.cpSync(
path.join(cwd, target),
path.join(tempDir, target),
{
filter: (src: string, dest: string) => {
console.log(
`cp: ${path.relative(cwd, src)} > ${dest}`
);
return true;
}
}
);
});
if (options.docsInputDir) {
console.log(`${SCRIPTNAME}: docs supplied, will copy...`);
// TODO: sync file stats
fs.cpSync(
path.join(cwd, options.docsInputDir),
path.join(tempDir, options.docsOutputDirname),
{
recursive: true,
filter: (src: string, dest: string) => {
console.log(
`cp: ${path.relative(cwd, src)} > ${dest}`
);
return true;
}
}
);
}
else {
console.log(`${SCRIPTNAME}: no docs supplied, will not copy...`);
}
console.log(`${SCRIPTNAME}: copying build output...`);
fs.cpSync(
options.inputDir,
path.join(tempDir, options.libOutputDirname),
{
recursive: true,
filter: (src: string, dest: string) => {
console.log(
`cp: ${path.relative(cwd, src)} > ${dest}`
);
return true;
}
}
);
const outputDir = path.resolve(cwd, options.outputDir);
console.log(`mkdir: ${outputDir}`);
fs.mkdirSync(outputDir, { recursive: true });
console.log(`npm: pack --pack-destination ${outputDir}`);
child_process.execSync(
`npm pack --pack-destination ${outputDir}`,
{
cwd: tempDir,
stdio: "inherit"
}
);
}
if (require.main === module) {
// minimum number of positional arguments
const minPosargs: number = 2;
// default values of options arguments
const defaultOptargs: {[key: string]: any} = {
'docs-dirname': DEFAULT_DOCS_DIRNAME,
'lib-dirname': DEFAULT_LIB_DIRNAME,
};
// required options arguments
const requiredOptargs: string[] = [
'docs-dirname',
'lib-dirname',
];
// the interface of parseArgs is very simple and Typescript does not play
// nicely with it, since it expects any reassignments to be of the same type
// as the primitives parseArgs allows. That's why I'm doing a lot of `as
// unknown as whatever` kung-fu down below. The node runtime doesn't care
// anyway...
var {values, positionals} = util.parseArgs({
options: {
'docs-dirname': {
type: 'string',
short: 'd'
},
'lib-dirname': {
type: 'string',
short: 'd'
},
'help': {
type: 'boolean',
short: 'h'
}
},
allowPositionals: true
});
// there's probably a prettier way as to not have to reassign this just to
// make tsc happy, but I'm a little exhausted...
const args: string[] = positionals;
if (values.help != undefined) {
const exec = [
'ts-node',
path.join(path.basename(__dirname), path.basename(__filename))
].join(' ');
console.log(usage(exec));
process.exit(1);
}
values = {...defaultOptargs, ...values};
var errors: boolean = false;
for (var requiredOptarg of requiredOptargs) {
if (!(requiredOptarg in values)) {
console.error(
`error: missing options argument: --${requiredOptarg}`
);
errors = true;
}
}
if (positionals.length < minPosargs) {
if (positionals.length == 1) {
positionals.push(DEFAULT_OUTPUT_DIR);
}
else if (positionals.length == 0) {
positionals.push(DEFAULT_INPUT_DIR);
positionals.push(DEFAULT_OUTPUT_DIR);
}
}
if (errors != false) {
console.log(`supply -h/--help, for more information.`)
process.exit(1);
}
pack({
inputDir: positionals[0],
outputDir: positionals[1],
docsInputDir: positionals[2] ?? null,
docsOutputDirname: values['docs-dirname']!,
libOutputDirname: values['lib-dirname']!,
});
}