From 2d735d9103c6ee741fbe5b03026e623b58072a49 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sun, 8 Feb 2026 23:28:09 +0800 Subject: [PATCH] add build.ts --- .gitignore | 1 + bundles/hello_world.ts | 18 ++++- script-meta-v2.json | 9 +++ single-scripts/build.ts | 147 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 172 insertions(+), 3 deletions(-) create mode 100755 single-scripts/build.ts diff --git a/.gitignore b/.gitignore index c21b004..ae43b4f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ .AppleDouble .LSOverride bundles/*.bundle.ts +bundles/*.bundle.ts.source-meta # Icon must end with two \r Icon diff --git a/bundles/hello_world.ts b/bundles/hello_world.ts index bd97282..d433046 100644 --- a/bundles/hello_world.ts +++ b/bundles/hello_world.ts @@ -1,10 +1,22 @@ -#!/usr/bin/env runts -- --allow-import +#!/usr/bin/env runts -- --allow-all -import {log} from "../libraries/deno-commons-mod.ts"; +import {log, term} from "../libraries/deno-commons-mod.ts"; // deno bundle --allow-import hello_world.ts -o helloworld-bundle-2.bundle.ts async function main() { - log.info("Hello World!"); + log.success("Hello World!"); + console.log(term.auto("\n[bg_white][black]Message:\n")); + console.log( + term.auto( + "[[[[bg_green][bold][red] MESSAGE [///] [green][bold]Hello[//] [red]world[/]. [under] under [/] [bold]BOLD[/]]]]", + ), + ); + console.log(term.auto("\n[bg_white][black]Prints as:\n")); + console.log( + term.auto( + "[bg_green][bold][red] MESSAGE [///] [green][bold]Hello[//] [red]world[/]. [under] under [/] [bold]BOLD[/]", + ), + ); } main().catch(console.error); diff --git a/script-meta-v2.json b/script-meta-v2.json index d274aeb..ce0e843 100644 --- a/script-meta-v2.json +++ b/script-meta-v2.json @@ -16,6 +16,15 @@ "publish_time": 1758209484774, "update_time": 1758209484774 }, + "build.ts": { + "script_name": "build.ts", + "script_length": 4555, + "script_sha256": "b70337218e8e164ba58c47ede81fdd9015ae81dd7c2658214720cf3e6fd27794", + "script_full_url": "https://git.hatter.ink/hatter/ts-scripts/raw/branch/main/single-scripts/build.ts", + "single_script_file": true, + "publish_time": 1770564482429, + "update_time": 1770564482429 + }, "cal-bun.ts": { "script_name": "cal-bun.ts", "script_length": 427, diff --git a/single-scripts/build.ts b/single-scripts/build.ts new file mode 100755 index 0000000..73c8ca4 --- /dev/null +++ b/single-scripts/build.ts @@ -0,0 +1,147 @@ +#!/usr/bin/env runts -- --allow-all + +import {execCommandShell, log, term, uint8ArrayToHexString,} from "https://script.hatter.ink/@35/deno-commons-mod.ts"; +import {parseArgs} from "jsr:@std/cli/parse-args"; +import {existsPath, stringifyPretty, writeStringToFile,} from "../libraries/deno-commons-mod.ts"; +import {readFileToString} from "https://global.hatter.ink/script/get/@1/deno-commons-1.6.0-mod.ts"; + +async function main() { + const flags = parseArgs(Deno.args, { + boolean: [ + "help", + "no-min", + "skip-sign", + "force", + ], + alias: { + S: "skip-sign", + M: "no-min", + F: "force", + }, + }); + + if (flags.help) { + console.log(term.auto(`Usage: +[blue][bold]build.ts[//] --help +[blue][bold]build.ts[//] [--no-min] FILES`)); + return; + } + + if (flags._.length === 0) { + log.error(`No files specified`); + return; + } + flags.min = !flags["no-min"]; + + const filesCount = flags._.length; + log.info(`Total ${filesCount} file(s)`); + for (let i = 0; i < filesCount; i++) { + const file = flags._[i]; + if (file.includes(".bundle.")) { + log.warn(`Skip bundle file: ${file} #${i + 1} of ${filesCount}`); + continue; + } + log.info(`Building file: ${file} #${i + 1} of ${filesCount}`); + await buildFile(file, flags); + } +} + +function getBundleFilename(file: string): string { + const lastIndexOfDot = file.lastIndexOf("."); + if (lastIndexOfDot === -1) { + return `${file}.bundle.ts`; + } + return `${file.substring(0, lastIndexOfDot)}.bundle.${ + file.substring(lastIndexOfDot + 1) + }`; +} + +async function sha256OfString(input: string): Promise { + const data = new TextEncoder().encode(input); + const hashBuffer = await crypto.subtle.digest("SHA-256", data); + return uint8ArrayToHexString(new Uint8Array(hashBuffer)); +} + +interface SourceFileMeta { + sha256: string; + builtTime: number; + min: boolean; +} + +async function writeBundleSourceSha256( + sourceFile: string, + bundleFile: string, + flags: any, +) { + const sourceFileSha256 = `${bundleFile}.source-meta`; + const sourceFileContent = await readFileToString(sourceFile); + const sourceFileContentSha256 = await sha256OfString(sourceFileContent); + await writeStringToFile( + sourceFileSha256, + stringifyPretty({ + sha256: sourceFileContentSha256, + builtTime: Date.now(), + min: flags.min, + } as SourceFileMeta), + ); +} + +async function checkBundleSourceSha256( + sourceFile: string, + bundleFile: string, + flags: any, +) { + const sourceFileSha256 = `${bundleFile}.source-meta`; + if (!await existsPath(sourceFileSha256)) { + return false; + } + const sourceFileSha256Content = await readFileToString(sourceFileSha256); + const sourceFileSha256Meta = JSON.parse( + sourceFileSha256Content, + ) as SourceFileMeta; + if (sourceFileSha256Meta.min !== flags.min) { + return false; + } + const sourceFileContent = await readFileToString(sourceFile); + const sourceFileContentSha256 = await sha256OfString(sourceFileContent); + return sourceFileContentSha256 === sourceFileSha256Meta.sha256; +} + +async function buildFile(file: string, flags: any) { + const denoCmd = "deno"; + const bundleFile = getBundleFilename(file); + const denoBundleArgs = ["bundle"]; + if (flags.min) { + denoBundleArgs.push("--minify"); + } + denoBundleArgs.push(file); + denoBundleArgs.push("-o"); + denoBundleArgs.push(bundleFile); + + const isSourceFileMatches = await checkBundleSourceSha256( + file, + bundleFile, + flags, + ); + if (isSourceFileMatches && !flags.force) { + log.info(`Check file ${file} sha256 matches, skip build`); + return; + } + if (isSourceFileMatches) { + log.info(`Force build file is on`); + } + log.debug("Run deno build: ", denoCmd, denoBundleArgs); + await execCommandShell(denoCmd, denoBundleArgs); + if (flags["skip-sign"]) { + log.warn(`Skip signature for file: ${bundleFile}`); + } else { + await execCommandShell("script-sign.rs", [bundleFile]); + } + await execCommandShell("chmod", ["+x", bundleFile]); + await writeBundleSourceSha256(file, bundleFile, flags); +} + +await main(); + +// @SCRIPT-SIGNATURE-V1: yk-r1.ES256.20260208T232753+08:00.MEQCIDKxUBkD92GrszyWM6gC +// jCNOVqcEMUdDKgs6TJlFhRbXAiBzlvmlHCXGFZBCcjjKBjmXDYqdbnfTp6D1wlSFyhK/sA==