Skip to content

Commit

Permalink
- add the submodule /src/fs.ts that presents utility functions for …
Browse files Browse the repository at this point in the history
…writing to the filesystem.

- update the example build script `/examples/1_html/build.ts` to write the esbuild output to the filesystem.
  • Loading branch information
omar-azmi committed Nov 28, 2024
1 parent 653c11c commit 6fc69b8
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 9 deletions.
28 changes: 21 additions & 7 deletions examples/1_html/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { denoPlugins } from "jsr:@luca/[email protected]"
import { fromFileUrl } from "jsr:@std/path"
import esbuild from "npm:esbuild"
import { resolveAsUrl } from "../../src/deps.ts"
import { writeOutputFiles } from "../../src/fs.ts"
import { HtmlLoader } from "./loader.ts"


Expand All @@ -11,15 +12,15 @@ const
html_file_content = await (await fetch(html_file_path)).text()

const html_file_loader = new HtmlLoader({ path: fromFileUrl(html_file_path) })
const js_txt = await html_file_loader.parseToJs(html_file_content)
const html_in_js = await html_file_loader.parseToJs(html_file_content)

const results = await esbuild.build({
absWorkingDir: fromFileUrl(this_dir_path),
format: "esm",
target: "esnext",
platform: "browser",
stdin: {
contents: js_txt,
contents: html_in_js,
loader: "ts",
resolveDir: fromFileUrl(resolveAsUrl("./", html_file_path)),
sourcefile: fromFileUrl(html_file_path),
Expand All @@ -34,11 +35,24 @@ const results = await esbuild.build({
plugins: [...denoPlugins()],
})

const js_compiled_text = results.outputFiles[0].text
const html_compiled_text = await html_file_loader.unparseFromJs(js_compiled_text)
console.log("%c" + "bundled html file output:", "color: green; font-weight: bold;")
const
[html_in_js_compiled, ...other_output_files] = results.outputFiles,
html_compiled_text = await html_file_loader.unparseFromJs(html_in_js_compiled.text)

console.log("%c" + `bundled html file: "0", path: "${html_in_js_compiled.path}"`, "color: green; font-weight: bold;")
console.log(html_compiled_text)
results.outputFiles.slice(1).forEach((js_file, index) => {
console.log("%c" + `bundled js file: ${index}`, "color: green; font-weight: bold;")
other_output_files.forEach((js_file, index) => {
console.log("%c" + `bundled js file: "${index + 1}", path: "${js_file.path}"`, "color: green; font-weight: bold;")
console.log(js_file.text)
})

await writeOutputFiles([
{
path: html_in_js_compiled.path.replace(/\.js$/, ".html"),
text: html_compiled_text
}, ...other_output_files
], {
dir: "./output/",
log: "verbose",
dryrun: false,
})
4 changes: 2 additions & 2 deletions src/deps.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { json_parse, json_stringify, math_min, object_entries, object_fromEntries, object_keys, object_values, promise_all } from "jsr:@oazmi/[email protected]/alias"
export { getUriScheme, joinPaths, relativePath, resolveAsUrl } from "jsr:@oazmi/[email protected]/pathman"
export { console_log, json_parse, json_stringify, math_min, object_entries, object_fromEntries, object_keys, object_values, promise_all } from "jsr:@oazmi/[email protected]/alias"
export { ensureEndSlash, getUriScheme, joinPaths, pathToPosixPath, relativePath, resolveAsUrl, resolvePathFactory } from "jsr:@oazmi/[email protected]/pathman"
export { isArray, isString } from "jsr:@oazmi/[email protected]/struct"

/** flags used for minifying (or eliminating) debugging logs and asserts, when an intelligent bundler, such as `esbuild`, is used. */
Expand Down
108 changes: 108 additions & 0 deletions src/fs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/** this submodule contains utility functions for writing esbuild's virtual output to the filesystem.
*
* this submodule is separated from the rest since it performs filesystem operations, which is runtime-dependant,
* and we don't want to impact the portability of the main module.
*
* @module
*/

import { ensureFile } from "jsr:@std/[email protected]"
import { console_log, ensureEndSlash, pathToPosixPath, promise_all, resolvePathFactory } from "./deps.ts"


/** get the current working directory (`Deno.cwd`) in posix path format. */
export const getCwdPath = () => { return ensureEndSlash(pathToPosixPath(Deno.cwd())) }

/** resolve a file path so that it becomes absolute, with unix directory separator ("/").
* TODO: refactor the name `pathResolve` to `resolvePath`
*/
export const pathResolve = resolvePathFactory(getCwdPath)

/** the tuple description of a writable (or appendable) file.
* - the first entry of the array must describe the destination path of the file,
* relative to the directory defined in {@link CreateFilesConfig.dir}).
* - the second entry should be the file's contents, which can either be a `string` text, a `ReadableStream`, or a `Uint8Array` binary.
* - the third and optional entry lets you specify additional {@link Deno.WriteFileOptions | deno specific file writing options},
* such as `"append"` the new text, or permit the creation (`"create"`) of new file if it doesn't exist, etc...
*/
export type WritableFileConfig = [
destination: string,
content: string | Uint8Array,
options?: Deno.WriteFileOptions,
]

/** configuration options for {@link createFiles}. */
export interface CreateFilesConfig {
/** the desired output directory.
* if a relative path is provided, then it will be resolved as a path relative to Deno's current working directory. (which is generally where `deno.json` resides.)
*/
dir?: string

/** select logging level:
* - `false` or `"none"`: skip logging.
* - `true` or `"basic"`: log what is being carried out at the top level.
* - `"verbose"`: in addition to basic logging, it also logs which files/folders are being copied or generated.
*
* @defaultValue `"basic"`
*/
log?: boolean | "none" | "basic" | "verbose"

/** enable `dryrun` if you wish for nothing to be written onto the the filesystem.
*
* @defaultValue `false`
*/
dryrun?: boolean
}

/** the in-memory output file description generated by `esbuild`. */
export interface EsbuildOutputFile {
path: string
text?: string
contents?: Uint8Array
hash?: string
}

/** print some basic useful information on the console.
* the print will only appear if the logging-level is either set to `"basic"` or `"verbose"` via {@link setLog}
*/
const logBasic = (log_level: NonNullable<CreateFilesConfig["log"]>, ...data: any[]): void => {
if (log_level === "basic" || log_level === "verbose") { console_log(...data) }
}

/** print verbose details on the console.
* the print will only appear if the logging-level is either set to `"verbose"` via {@link setLog}
*/
const logVerbose = (log_level: NonNullable<CreateFilesConfig["log"]>, ...data: any[]): void => {
if (log_level === "verbose") { console_log(...data) }
}

/** write a collection of virtual files to your filesystem.
* this function accepts virtual files that are either in text (`string`), binary (`Uint8Array`), or streamable text/binary (`ReadableStream<string | Uint8Array>`) formats.
* it is important that you provide the configuration parameter's {@link config["dir"] | `dir`} field, so that relative paths can be resolved according to the provided directory.
*/
export const createFiles = async (virtual_files: Array<WritableFileConfig>, config: CreateFilesConfig = {}): Promise<void> => {
const { dir = "./", log = "basic", dryrun = false } = config
// writing text or binary files
logBasic(log, "[in-fs] writing additional text/binary files to your build directory")
await promise_all(virtual_files.map(async ([dst_path, content, options]) => {
const abs_dst = pathResolve(dir, dst_path)
logVerbose(log, `[in-fs] writing file to: "${abs_dst}"`, "with the configuration:", options)
if (!dryrun) {
await ensureFile(abs_dst)
if (typeof content === "string") {
await Deno.writeTextFile(abs_dst, content, options)
} else {
await Deno.writeFile(abs_dst, content, options)
}
}
}))
}

export const writeOutputFiles = async (virtual_files: Array<EsbuildOutputFile>, config: CreateFilesConfig = {}): Promise<void> => {
return createFiles(virtual_files.map((virtual_file): WritableFileConfig => {
const
{ path, contents, text } = virtual_file,
content = contents ?? text ?? ""
return [path, content]
}), config)
}

0 comments on commit 6fc69b8

Please sign in to comment.