-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Changed to cross-platform build-system
- Loading branch information
Jacob Schneider
authored and
Jacob Schneider
committed
Sep 13, 2024
1 parent
5bf6106
commit 147365a
Showing
7 changed files
with
315 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import * as fs from 'node:fs/promises'; | ||
import State from '@j-cake/jcake-utils/state'; | ||
import { iterSync } from '@j-cake/jcake-utils/iter'; | ||
import * as Format from '@j-cake/jcake-utils/args'; | ||
import chalk from 'chalk'; | ||
|
||
import log from './log.js'; | ||
import Path from './path.js'; | ||
import * as comp from './components.js'; | ||
|
||
export const config = new State({ | ||
logLevel: 'info', | ||
force: false, | ||
|
||
root: new Path(process.cwd()), | ||
out: new Path(process.cwd()).concat('build'), | ||
|
||
components: [] | ||
}); | ||
|
||
export default async function main(argv) { | ||
const logLevel = Format.oneOf(Object.keys(log), false); | ||
|
||
for (const { current: i, skip: next } of iterSync.peekable(argv)) | ||
if (i == '--log-level') | ||
config.setState({ logLevel: logLevel(next()) }); | ||
|
||
else if (i == '-f' || i == '--force') | ||
config.setState({ force: true }); | ||
|
||
else if (i == '-o' || i == '--out') | ||
config.setState({ out: new Path(next()) }); | ||
|
||
else | ||
config.setState(prev => ({ components: [...prev.components, i] })); | ||
|
||
log.debug(config.get()); | ||
|
||
await fs.mkdir(config.get().out.path, { recursive: true }); | ||
|
||
for (const component of config.get().components) | ||
if (component in components) | ||
await components[component]() | ||
.then(status => log.info(`${chalk.grey(component)}: Done`)); | ||
} | ||
|
||
export const components = { | ||
"build:plugin": () => comp.build_plugin(), | ||
"build:package.json": () => comp.build_package_json(), | ||
"build:manifest.json": () => comp.build_manifest_json(), | ||
"build:style.css": () => comp.build_style_css(), | ||
|
||
"phony:install": () => comp.phony_install(), | ||
"phony:all": () => Promise.all(Object.entries(components) | ||
.filter(([comp, _]) => comp.startsWith("build:")) | ||
.map(([_, fn]) => fn())), | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import * as fs from 'node:fs/promises'; | ||
import * as fss from 'node:fs'; | ||
import * as cp from 'node:child_process'; | ||
import esbuild from 'esbuild'; | ||
|
||
import { config } from './build.js'; | ||
import log from './log.js'; | ||
import { has_changed } from './util.js'; | ||
|
||
const is_source = path => path.startsWith(config.get().root.join("src")); | ||
|
||
export async function build_plugin() { | ||
if (!await has_changed({ | ||
glob: path => is_source(path), | ||
dependents: [config.get().out.join("main.js")] | ||
})) | ||
return log.verbose("Skipping Rebuild"); | ||
|
||
await esbuild.build({ | ||
entryPoints: ["src/main.ts"], | ||
bundle: true, | ||
sourcemap: true, | ||
platform: 'node', | ||
format: 'cjs', | ||
external: ['electron', 'obsidian'], | ||
outdir: config.get().out.path | ||
}); | ||
} | ||
|
||
export async function build_package_json() { | ||
if (!await has_changed({ | ||
glob: path => is_source(path), | ||
dependents: [config.get().out.join("package.json")] | ||
})) | ||
return log.verbose("Skipping Rebuild"); | ||
|
||
const jq = cp.spawn('jq', ['-r', '. * .deploy * {deploy:null} | with_entries(select(.value |. != null))']); | ||
|
||
fss.createReadStream(config.get().root.join("package.json").path) | ||
.pipe(jq.stdin); | ||
|
||
jq.stdout.pipe(fss.createWriteStream(config.get().out.join("package.json").path), 'utf8'); | ||
|
||
await new Promise((ok, err) => jq.on("exit", code => code == 0 ? ok() : err(code))); | ||
} | ||
|
||
export async function build_manifest_json() { | ||
if (!await has_changed({ | ||
glob: path => is_source(path), | ||
dependents: [config.get().out.join("manifest.json")] | ||
})) | ||
return log.verbose("Skipping Rebuild"); | ||
|
||
const jq = cp.spawn('jq', ['-r', '.']); | ||
|
||
fss.createReadStream(config.get().root.join("manifest.json").path) | ||
.pipe(jq.stdin); | ||
|
||
jq.stdout.pipe(fss.createWriteStream(config.get().out.join("manifest.json").path), 'utf8'); | ||
|
||
await new Promise((ok, err) => jq.on("exit", code => code == 0 ? ok() : err(code))); | ||
} | ||
|
||
export async function build_style_css() { | ||
if (!await has_changed({ | ||
glob: path => is_source(path), | ||
dependents: [config.get().out.join("styles.css")] | ||
})) | ||
return log.verbose("Skipping Rebuild"); | ||
|
||
await esbuild.build({ | ||
entryPoints: ["styles.css"], | ||
bundle: true, | ||
sourcemap: true, | ||
outdir: config.get().out.path | ||
}); | ||
} | ||
|
||
export async function phony_install() { | ||
const pkg = await fs.readFile(config.get().root.join("package.json")) | ||
.then(pkg => JSON.parse(pkg).name); | ||
|
||
const install = config.get().vault.join(".obsidian/plugins").join(pkg).path; | ||
|
||
await fs.mkdir(install, { recursive: true }); | ||
|
||
for await (const file of config.get().out.readdir()) | ||
await fs.copyFile(file.path, install); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import util from 'node:util'; | ||
import chalk from 'chalk'; | ||
|
||
import { config } from './build.js'; | ||
|
||
export const stripAnsi = str => str.replace(/[\u001b\u009b][[()#;?]*(?:\d{1,4}(?:;\d{0,4})*)?[\dA-ORZcf-nqry=><]/g, ''); | ||
export const centre = (text, width) => { | ||
const colourless = stripAnsi(text); | ||
const pad = Math.floor((width - colourless.length) / 2); | ||
return `${' '.repeat(pad)}${text}${' '.repeat(pad)}`.padStart(width, ' '); | ||
} | ||
export function stdout(tag, ...msg) { | ||
const log = msg | ||
.map(i => ['string', 'number', 'bigint', 'boolean'].includes(typeof i) ? i : util.inspect(i, false, null, true)) | ||
.join(' ') | ||
.split('\n') | ||
.map((i, a) => `${a ? centre('\u2502', stripAnsi(tag).length) : tag} ${i}\n`); | ||
|
||
for (const i of log) | ||
process.stdout.write(i); | ||
} | ||
|
||
export function stderr(tag, ...msg) { | ||
const log = msg | ||
.map(i => ['string', 'number', 'bigint', 'boolean'].includes(typeof i) ? i : util.inspect(i, false, null, true)) | ||
.join(' ') | ||
.split('\n') | ||
.map((i, a) => `${a ? centre('\u2502', stripAnsi(tag).length) : tag} ${i}\n`); | ||
|
||
for (const i of log) | ||
process.stderr.write(i); | ||
} | ||
|
||
export default { | ||
err: (...arg) => void (['err', 'info', 'verbose', 'debug'].includes(config.get().logLevel) && stderr(chalk.grey(`[${chalk.red('Error')}]`), ...arg)), | ||
info: (...arg) => void (['info', 'verbose', 'debug'].includes(config.get().logLevel) && stdout(chalk.grey(`[${chalk.blue('Info')}]`), ...arg)), | ||
verbose: (...arg) => void (['verbose', 'debug'].includes(config.get().logLevel) && stdout(chalk.grey(`[${chalk.yellow('Verbose')}]`), ...arg)), | ||
debug: (...arg) => void (['debug'].includes(config.get().logLevel) && stdout(chalk.grey(`[${chalk.cyan('Debug')}]`), ...arg)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import * as fs from 'node:fs/promises'; | ||
import * as fss from 'node:fs'; | ||
import * as path from 'node:path'; | ||
|
||
import log from './log.js'; | ||
|
||
export default class Path { | ||
constructor(str) { | ||
if (str instanceof fss.Dirent) | ||
this.path = path.join(str.parentPath, str.name); | ||
else if (str instanceof Path) | ||
this.path = str.path; | ||
else | ||
this.path = path.isAbsolute(str) ? str : path.join(process.cwd(), str); | ||
} | ||
|
||
concat(...paths) { | ||
this.path = path.join(this.path, ...paths.map(i => i instanceof Path ? i.path : i)); | ||
return this; | ||
} | ||
|
||
join(...paths) { | ||
return new Path(path.join(this.path, ...paths.map(i => i instanceof Path ? i.path : i))); | ||
} | ||
|
||
ext() { | ||
return this.path.split(path.sep).pop()?.split('.').pop()?.toLowerCase() ?? ''; | ||
} | ||
|
||
async *readdir(recursive = true) { | ||
const files = await fs.readdir(this.path, { recursive, withFileTypes: true }) | ||
|
||
for (const dirent of files) | ||
yield new Path(dirent); | ||
|
||
// for await (const dir of await fs.readdir(config.get().root.path, { recursive: true, withFileTypes: true })) | ||
// if (dir.isFile()) | ||
// log.info(new Path(dir)); | ||
} | ||
|
||
async mtime() { | ||
return await fs.stat(this.path).then(stat => stat.mtime); | ||
} | ||
|
||
async isFile() { | ||
return await fs.stat(this.path).then(stat => stat.isFile()); | ||
} | ||
|
||
async isDir() { | ||
return await fs.stat(this.path).then(stat => stat.isDirectory()); | ||
} | ||
|
||
async isBlockdev() { | ||
return await fs.stat(this.path).then(stat => stat.isBlockDevice()); | ||
} | ||
|
||
async isChardev() { | ||
return await fs.stat(this.path).then(stat => stat.isCharacterDevice()); | ||
} | ||
|
||
async isPipe() { | ||
return await fs.stat(this.path).then(stat => stat.isFIFO() || stat.isSocket()); | ||
} | ||
|
||
async isFifo() { | ||
return await fs.stat(this.path).then(stat => stat.isFIFO()); | ||
} | ||
|
||
async isSocket() { | ||
return await fs.stat(this.path).then(stat => stat.isSocket()); | ||
} | ||
|
||
async isSymlink() { | ||
return await fs.stat(this.path).then(stat => stat.isSymbolicLink()); | ||
} | ||
|
||
replaceBase(base, newBase) { | ||
if (this.path.startsWith(base.path)) | ||
return newBase.join(this.path.slice(base.path.length)); | ||
|
||
else | ||
throw { | ||
err: 'Could not substitute path', | ||
reason: `Path does not start with '${base.path}'`, | ||
path: this | ||
}; | ||
} | ||
|
||
parent() { | ||
return new Path(this.path.split(path.sep).slice(0, -1).join(path.sep)); | ||
} | ||
|
||
startsWith(base) { | ||
const chunks = this.path.split(path.sep); | ||
return base.path.split(path.sep).every(i => chunks.shift() == i); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { config } from './build.js'; | ||
|
||
const file_tree = async function() { | ||
const files = []; | ||
|
||
if (files.length == 0) | ||
for await (const path of config.get().root.readdir()) | ||
files.push(path); | ||
|
||
return files; | ||
} | ||
|
||
export async function* get_files(glob) { | ||
for (const path of await file_tree()) | ||
if (await glob(path)) | ||
yield path; | ||
} | ||
|
||
export async function has_changed(glob) { | ||
if (config.get().force) | ||
return true; | ||
|
||
const mtimes = await Promise.all(glob.dependents.map(i => i.mtime().catch(_ => new Date(0)))); | ||
|
||
for await (const file of get_files(glob.glob)) | ||
if (await Promise.race(mtimes.map(async i => await file.mtime() > i))) | ||
return true; | ||
|
||
return false; | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters