From 78a2de51a130629745e902a526bf023e07809ff3 Mon Sep 17 00:00:00 2001 From: KimlikDAO-bot Date: Tue, 27 Aug 2024 23:10:40 -0700 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=A6=20Add=20`kdjs`=20usage=20and=20dec?= =?UTF-8?q?larative=20worker=20build?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- birimler/builder.js | 63 ++++++++++++++++++++++++++++++++++++++++++ birimler/js/targets.js | 30 ++++++++++++++++++++ birimler/js/util.js | 46 ++++++++++++++++++++++++++++-- crypto/sha3.js | 2 ++ kdjs/README.md | 50 ++++++++++++++++++++++++++++++--- kdjs/compile.js | 4 ++- kdjs/kdjs.js | 20 +++++++++++++- kdjs/package.json | 6 ++-- 8 files changed, 209 insertions(+), 12 deletions(-) create mode 100755 birimler/builder.js create mode 100644 birimler/js/targets.js diff --git a/birimler/builder.js b/birimler/builder.js new file mode 100755 index 0000000..7c6ed3b --- /dev/null +++ b/birimler/builder.js @@ -0,0 +1,63 @@ +#!/usr/bin/env bun +import yaml from "js-yaml"; +import { readFile, readdir } from "node:fs/promises"; +import { uploadWorker } from "./cloudflare/targets"; +import { compileWorker } from "./js/targets"; + +/** @define {string} */ +const ROOT_PATH = "../../.."; + +/** + * @param {string} dirName + * @return {!Promise} + */ +const readBuildRecipe = (dirName) => readdir(dirName) + .then((/** !Array */ dir) => { + for (const file of dir) { + if (!file.startsWith(".") && file.endsWith(".yaml")) + return readFile(`${dirName}/${file}`) + .then((content) => yaml.load(content)); + } + return Promise.reject(); + }); + +/** + * @param {string} dirName + * @param {!Object} env + */ +const buildCrate = (dirName, env) => import(`${ROOT_PATH}/${dirName}/build.js`) + .then((buildFile) => buildFile.default && buildFile.default.build + ? buildFile.default.build(env) + : Promise.reject()) + .catch(() => readBuildRecipe(dirName) + .then((recipe) => { + if (recipe.worker) + compileWorker(dirName, recipe.worker, env); + }) + ); + +/**s + * @param {string} dirName + * @param {!Object} env + */ +const deployCrate = (dirName, env) => import(`${dirName}/build.js`) + .then( + (buildFile) => buildFile.default.deploy(env), + () => readBuildRecipe(dirName) + .then((recipe) => { + if (recipe.worker) + return compileWorker(dirName, recipe.worker) + .then((code) => uploadWorker(env.cloudflare.auth, recipe.worker.name, code)) + }) + ); + +/** @dict */ +const env = yaml.load(await readFile(".gizli.yaml", "utf8").catch(() => "")); +/** @const {string} */ +const crate = process.argv[2] || "."; +/** @const {boolean} */ +const isDeploy = process.argv[3] == "deploy"; + +isDeploy + ? deployCrate(crate, env) + : buildCrate(crate, env); diff --git a/birimler/js/targets.js b/birimler/js/targets.js new file mode 100644 index 0000000..5693e3c --- /dev/null +++ b/birimler/js/targets.js @@ -0,0 +1,30 @@ +import { compile } from "../../kdjs/compile"; +import { selectDefines, ensureDotJs } from "./util"; + +/** + * @param {string} dirName + * @param {{ + * entry: string, + * defines: !Array + * }} config + * @param {!Object=} env + * @return {!Promise} + */ +const compileWorker = async (dirName, config, env) => { + /** @const {string} */ + const entry = ensureDotJs(`${dirName}/${config.entry}`); + const compileParams = { + entry, + output: `build/${entry}`, + }; + if (config.defines) + compileParams.define = selectDefines(entry.slice(0, -3), config.defines, env); + return compile(compileParams); +} + +export { + compileWorker +}; diff --git a/birimler/js/util.js b/birimler/js/util.js index 5acad09..224337c 100644 --- a/birimler/js/util.js +++ b/birimler/js/util.js @@ -1,5 +1,18 @@ +import yaml from "js-yaml"; import { readFile } from "node:fs/promises"; -import toml from "toml"; +import { combine } from "../../util/paths"; + +/** + * @param {string} str + * @return {string} + */ +const ensureDotJs = (str) => str.endsWith(".js") ? str : str + ".js"; + +/** + * @param {string} str + * @return {string} + */ +const removeDotJs = (str) => str.endsWith(".js") ? str.slice(0, -3) : str; /** * @param {string} definesFile @@ -10,12 +23,33 @@ const readDefines = (definesFile, module) => readFile(definesFile) .then( (fileContent) => { const moduleName = module.replaceAll("/", "$"); - return Object.entries(toml.parse(fileContent)).map( + return Object.entries(yaml.load(fileContent)).map( ([key, value]) => `${key}$$module$${moduleName}="${value}"`) }, () => [] ) +const selectDefines = (module, defines, env) => { + const out = []; + for (const define of defines) { + const [mod, key] = typeof define == "string" + ? [module, define] + : [removeDotJs(define.module.startsWith("/") + ? define.module.slice(1) + : combine(module, "../" + define.module)), define.value]; + + let val = env; + if (env) + for (const k of key.split(".")) + val = val[k]; + + /** @const {string} */ + const varName = key.replaceAll(".", "_").toUpperCase(); + out.push(`${varName}$$module$${mod.replaceAll("/", "$")}=${JSON.stringify(val)}`); + } + return out; +} + /** * @param {string} module * @param {string} name @@ -25,4 +59,10 @@ const readDefines = (definesFile, module) => readFile(definesFile) const define = (module, name, value) => `${name}$$module$${module.replaceAll("/", "$")}=${value}`; -export { define, readDefines }; +export { + define, + ensureDotJs, + readDefines, + removeDotJs, + selectDefines +}; diff --git a/crypto/sha3.js b/crypto/sha3.js index c68d132..214b7bf 100644 --- a/crypto/sha3.js +++ b/crypto/sha3.js @@ -40,6 +40,8 @@ const keccak256Uint32ToHex = (words) => hex.from( new Uint8Array(keccak256Uint32(words).buffer, 0, 32)); /** + * @pureOrBreakMyCode + * @nosideeffects * @param {string} str A string to be hashed. * @return {string} hex encoded hash. */ diff --git a/kdjs/README.md b/kdjs/README.md index 1081ab7..ed44424 100644 --- a/kdjs/README.md +++ b/kdjs/README.md @@ -1,10 +1,52 @@ -# KimlikDAO js compiler +# `kdjs`: KimlikDAO JavaScript compiler -Primarily wraps Google Closure Compiler (GCC) and UglifyJS but adds additional functionality -to GCC such as ability to compile es6 modules. +`kdjs` is a javascript compiler with advanced optimization and minification capabilities. +It primarily builds upon the Google Closure Compiler (GCC) however it has many custom optimization +passes and a more powerful module system. + +For instance, `kdjs` it is able to generate es6 modules with `export`s and unlinked `import`s, which +is not possible with GCC. + +`kdjs` expects your code to be fully annotated using the Google Closure Compiler type annotations. +Just like in GCC, your type annotations can be either in a `.js` file or an externs file, which in `kdjs` +has to end with the extension `.d.js`. + +You can run `kdjs` directly ```shell bun kdjs/kdjs.js entry.js ``` -`kdjs` will automatically crawl all the imported files from the entry.js and include the externs files for libraries that it recognizes. +or install it system wide + +```shell +cd kdjs +npm i -g . +``` + +When you run `kdjs` with a supplied entry file like so + +```shell +$ kdjs entry.js +``` + +it will automatically crawl all the imported files from the entry.js and +include the externs files for libraries that it recognizes. + +```shell +$ kdjs + +kdjs 0.0.1 + +Usage: kdjs entry.js [parameters] + +Parameters: + --output (-o) : The name of the output file + --print : Print the compiled code to stdout + --strict : Report unknown type + --loose : Don't perform strictTypeCheck + --nologs : Strip all console.log() calls + --define : Values for @define annotated variables + --isolateDir : Directory name to write the isolated and preprocessed input files + --emit_shebang : Whether to place bun shebang sequence at the beginning of the output +``` diff --git a/kdjs/compile.js b/kdjs/compile.js index 5a1f7d5..c6496bb 100644 --- a/kdjs/compile.js +++ b/kdjs/compile.js @@ -85,6 +85,7 @@ const compile = async (params, checkFreshFn) => { }, toplevel: true, compress: { + // evaluate: "eager", module: true, toplevel: true, passes: 3, @@ -97,8 +98,9 @@ const compile = async (params, checkFreshFn) => { console.log(`Uglified size:\t${uglified.code.length}\nGCC size:\t${output.length}`); let code = uglified.code.length < output.length ? uglified.code : output; if (/** @type {boolean} */(params["emit_shebang"])) - code = "#!/usr/bin/env node\n" + code; + code = "#!/usr/bin/env bun\n" + code; console.log(uglified.warnings, uglified.error); + if (params["print"]) console.log(code); if (params["output"]) writeFile(/** @type {string} */(params["output"]), code) .then(() => resolve(code)) diff --git a/kdjs/kdjs.js b/kdjs/kdjs.js index e7f192b..dbdfa4f 100644 --- a/kdjs/kdjs.js +++ b/kdjs/kdjs.js @@ -1,6 +1,5 @@ #!/usr/bin/env node -import { writeFile } from "node:fs/promises"; import process from "node:process"; import { parseArgs } from "../util/cli"; import { compile } from "./compile"; @@ -9,6 +8,25 @@ const params = parseArgs(process.argv.slice(2), "entry", { "-o": "output", "-d": "exports", }); + +if (typeof params["entry"] != "string") { + console.log(`kdjs 0.0.1 + +Usage: kdjs entry.js [parameters] + +Parameters: + --output (-o) : The name of the output file + --print : Print the compiled code to stdout + --strict : Report unknown type + --loose : Don't perform strictTypeCheck + --nologs : Strip all console.log() calls + --define : Values for @define annotated variables + --isolateDir : Directory name to write the isolated and preprocessed input files + --emit_shebang : Whether to place bun shebang sequence at the beginning of the output +`); + process.exit(0); +} + params["output"] ||= /** @type {string} */(params["entry"]) .replace(/\.js$/, ".out.js"); diff --git a/kdjs/package.json b/kdjs/package.json index 571b802..457f62a 100644 --- a/kdjs/package.json +++ b/kdjs/package.json @@ -24,10 +24,10 @@ "optimize" ], "dependencies": { - "acorn": "^8.12.0", + "acorn": "^8.12.1", "acorn-walk": "^8.3.3", - "astring": "^1.8.6", + "astring": "^1.9.0", "google-closure-compiler": "^20240317.0.0", - "uglify-js": "^3.18.0" + "uglify-js": "^3.19.2" } } \ No newline at end of file