Setting up a base project for this.

master
Zed A. Shaw 2 years ago
parent 98f11eca8f
commit d0a63a7acf
  1. 62
      bando.js
  2. 5
      index.js
  3. 99
      lib/builderator.js
  4. 32
      lib/logging.js
  5. 3611
      package-lock.json
  6. 14
      package.json
  7. 6
      tests/commands/install.js

@ -0,0 +1,62 @@
#!/usr/bin/env node
import { Command } from "commander";
// NOTE: this changed in node 18 to be node:url for...stupid
import { fileURLToPath } from "url";
import { glob } from "./lib/builderator.js";
import path from "path";
import assert from "assert";
import fs from "fs";
// BUG: this will load the commands but most of them assume the root
const __filename = fileURLToPath(import.meta.url);
const { dir } = path.parse(__filename);
let __dirname = dir;
// now even exec commands will know where the project root is
process.env['PROJECT_ROOT'] = __dirname;
const program = new Command();
// remember that glob doesn't like windows \
const command_dir = `${__dirname}/commands`;
if(!fs.existsSync(command_dir)) {
console.error("ERROR: there is no command directory at", command_dir);
process.exit(1);
}
program
.name("bando")
.description("Command runner for the bando project.")
.version("0.1.0");
for(let command of glob(`${command_dir}/*.js`)) {
const { name } = path.parse(command);
const mod = await import(command);
assert(mod.description, `export const description missing in ${command}`);
assert(mod.main, `export const main missing in ${command}`);
const build = program.command(name)
.description(mod.description)
.action(mod.main);
if(mod.options) {
mod.options.forEach(opt => build.option(...opt));
}
if(mod.required) {
mod.required.forEach(opt => build.requiredOption(...opt));
}
if(mod.argument) {
build.argument(...mod.argument);
}
}
try {
program.parse();
} catch(error) {
console.log("------------- ERROR, here's the stack trace.");
console.error(error.stack);
process.exit(1);
}

@ -1,5 +0,0 @@
#!/usr/bin/env node
import { writeFile } from "node:fs/promises";
await writeFile("test.txt", "whatever");

@ -0,0 +1,99 @@
import fg from "fast-glob";
import fs from "fs";
import assert from "assert";
import { log } from "./logging.js";
import Path from "path";
import { execSync } from "child_process";
// fast-glob is old and can't do import as
export const glob = (path) => {
if(process.platform === "win32") {
// fast-glob doesn't understand \\ or C:\
const fixed_path = path.replace("C:","").replaceAll('\\', '/')
return fg.sync(fixed_path);
} else {
return fg.sync(path);
}
}
export const changed = (source, target) => {
// we want the source to error if it doesn't exist
const s_stat = fs.statSync(source);
// but target might not exist, which would mean it's a change
const t_stat = fs.statSync(target, {throwIfNoEntry: false});
return !t_stat || s_stat.mtimeMs > t_stat.mtimeMs;
}
export const mkdir = (dir) => {
/* Makes a directory recursively. */
if(!fs.existsSync(dir)) {
log.debug(`making dir ${dir}`);
fs.mkdirSync(dir, { recursive: true });
}
}
/**
* Given any path to a file, makes sure all its directories are in place.
*
* @param { string } -- path to file
*/
export const mkdir_to = (target) => {
let dirs = Path.parse(target);
mkdir(dirs.dir);
}
export const write = (target, data) => {
/* Convenience function that makes sure the dir is there then writes the data. */
mkdir_to(target);
fs.writeFileSync(target, data);
}
export const copy = (src, dest, filter=null) => {
/** Like write but does an OS copy after making the target dir. When given a
* filter it will give the filter function the arguments, let it return contents,
* then write those instead of a copy.
*/
mkdir_to(dest);
if(filter) {
try {
let raw_data = fs.readFileSync(src);
let data = filter(src, dest, raw_data);
write(dest, data);
} catch (error) {
log.error(error, "Problem with filter write in copy");
}
} else {
fs.copyFileSync(src, dest);
}
}
export const rm = (file_name, ignore=false) => {
log.warn("Deleting file", file_name);
try {
fs.unlinkSync(file_name);
} catch(error) {
if(ignore) {
log.debug(`Requested delete file ${file_name} doesn't exist, but ignored.`);
} else {
log.error(error, file_name);
throw error;
}
}
}
export const exec = (cmd, opts) => {
log.info(`EXEC ${cmd}`);
return execSync(cmd, opts);
}
export const exec_i = (cmd, opts={}) => {
return exec(cmd, {stdio: [process.stdout, process.stderr, process.stdin], ...opts});
}
export default {
rm, exec, changed, mkdir, mkdir_to, write, copy, exec_i, glob
}

@ -0,0 +1,32 @@
import pino from 'pino';
import pinoPretty from 'pino-pretty';
import path from 'path';
const log_level = process.env.PROD === undefined ? "debug" : "info";
const pino_config = {
level: log_level,
}
if(!process.env.PROD) {
pino_config.transport = {
target: 'pino-pretty',
options: {
levelFirst: true,
colorize: true,
singleLine: true,
ignore: "module",
messageFormat: "{levelLabel}[{pid}] {module}: {msg}"
}
}
}
export const log = pino(pino_config);
export const create = (import_url) => {
const pd = path.parse(import_url);
const log_line = `${path.basename(pd.dir)}/${pd.base}`;
return log.child({module: log_line});
}
export default { create, log };

3611
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -2,13 +2,9 @@
"name": "create-npm-testing", "name": "create-npm-testing",
"version": "1.0.0", "version": "1.0.0",
"description": "Testing npm install/create.", "description": "Testing npm install/create.",
"main": "index.js", "main": "bando.js",
"bin": {
"create-npm-testing": "index.js",
"create": "index.js"
},
"scripts": { "scripts": {
"test": "test" "test": "ava tests/**/*.js"
}, },
"type": "module", "type": "module",
"repository": { "repository": {
@ -21,6 +17,10 @@
"author": "Zed A. Shaw", "author": "Zed A. Shaw",
"license": "0BSD", "license": "0BSD",
"dependencies": { "dependencies": {
"commander": "^9.4.1" "ava": "^5.1.0",
"commander": "^9.4.1",
"fast-glob": "^3.2.12",
"pino": "^8.7.0",
"pino-pretty": "^9.1.1"
} }
} }

@ -0,0 +1,6 @@
import test from "ava";
test("base test setup", (t) => {
t.pass();
});
Loading…
Cancel
Save