diff --git a/deno.json b/deno.json index 740014f..138862c 100644 --- a/deno.json +++ b/deno.json @@ -13,6 +13,8 @@ }, "exports": { ".": "./src/mod.ts", + "./loader": "./src/loader.ts", + "./fs": "./src/fs.ts", "./funcdefs": "./src/funcdefs.ts", "./typedefs": "./src/typedefs.ts" }, @@ -36,11 +38,13 @@ "semiColons": false, "singleQuote": false, "lineWidth": 800, - "proseWrap": "never", + "proseWrap": "preserve", "include": [ "./src/", "./examples/", - "./test/" + "./test/", + "./*.md", + "./*.json" ] }, "compilerOptions": { diff --git a/readme.md b/readme.md index b5c98f7..e86e056 100644 --- a/readme.md +++ b/readme.md @@ -2,8 +2,82 @@ A utility library for building generic file loading plugins for esbuild that are compatible with `Deno` runtime and its esbuild plugin ([`jsr:@luca/esbuild-deno-loader`](https://jsr.io/@luca/esbuild-deno-loader)). -## Super Mandatory Example +## Super mandatory example ```ts // TODO: never ``` + +## Documentation link + +[`github pages`](https://oazmi.github.io/esbuild-generic-loader/) + +## How the loader works: + +To put it simply, a subclass of {@link "loader"!GenericLoader} performs the following steps in order: + +1. {@link "loader"!GenericLoader.extractDeps} parses the dependencies of the provided `content`. +2. {@link "loader"!GenericLoader.parseToJs} creates a javascript-code that dynamically imports the dependencies, and exports the original `content`. +3. [**you**](https://en.wikipedia.org/wiki/human) pass the javacscript-code to `esbuild` for bundling and transformation of the import statements. +4. {@link "loader"!GenericLoader.unparseFromJs} parses the resulting output javascript-code and extracts the new path names of the dependencies. +5. {@link "loader"!GenericLoader.injectDeps} merges back the parsed dependencies to the original `content`. + +## Loader usage example: + +Here is how you would typically use a subclass of the {@link "loader"!GenericLoader}: + +- instantiate a {@link "loader"!GenericLoader} instance with optional config (which currently does nothing). + +```ts +// make sure that you have extended `GenericLoader` and redefined the abstract methods +class MyLoader extends GenericLoader {} + +const my_file_loader = new MyLoader({ + path: "D:/my/project/my_file.xyz", +}) +``` + +- convert the contents of the file you wish to bundle to equivalent javascript code using the {@link "loader"!GenericLoader.parseToJs} method. + +```ts +const js_content = await my_file_loader.parseToJs() +``` + +- pass the js content to your esbuild plugin's `onLoad` result, or use it as an entrypoint via `stdin`. + +```ts +const build_result = await esbuild.build({ + absWorkingDir: "D:/my/project/", + splitting: true, // required, so that the bundled `js_content` imports the referenced dependency files, instead of having them injected. + format: "esm", // required for the `splitting` option to work + bundle: true, // required, otherwise all links/dependencies will be treated as "external" and won't be transformed. + outdir: "./out/", // required, for multiple output files + write: false, // required, because the bundled content needs to exist in-memory for us to transform/unparse it back to its original form. + minify: true, // optiotnal, useful for treeshaking. + chunkNames: "[ext]/[name]-[hash]", // optional, useful for specifying the structure of the output directory + assetNames: "assets/[name]-[hash]", // optional, useful for specifying the structure of the output directory + plugins: [...denoPlugins()], // optional, use the Deno esbuild plugin to resolve "http://", "file://", "jsr:", and "npm:" imports. + stdin: { + contents: js_content, + loader: "ts", + resolveDir: "D:/my/project/", + sourcefile: "D:/my/project/my_file.xyz", + }, +}) +``` + +- once the build is complete, convert back the bundled entrypoint from javascript to your file's format using the {@link "loader"!GenericLoader.unparseFromJs} method. + +```ts +const js_content_bundled = build_result.outputFiles[0].text // assuming that the first output file corresponds to your entrypoint +const my_file_bundled = await my_file_loader.unparseFromJs(js_content_bundled) +``` + +- merge back the string contents of `my_file_bundled` to `build_results.outputFiles`, + and then write the outputs to the filesystem using the {@link "fs"!writeOutputFiles} utility function. + +```ts +const { hash, path } = outputs.outputFiles[0] +build_result.outputFiles[0] = { text: my_file_bundled, hash, path } +await writeOutputFiles(outputs.outputFiles) +``` diff --git a/src/loader.ts b/src/loader.ts index fbba51a..b3723ec 100644 --- a/src/loader.ts +++ b/src/loader.ts @@ -1,55 +1,74 @@ /** a generic esbuild loader. * - * ## How it works: - * - TODO: explain - * - * ## Usage procedure: - * - instantiate a `GenericLoader` instance with the contents of the file you wish to bundle. - * ```ts - * // make sure that you have extended `GenericLoader` and redefined the abstract methods - * class MyLoader extends GenericLoader { } - * - * const my_file_loader = new MyLoader( - * `include abc from "./another_file.ts"; abc();`, - * { path: "D:/my/project/my_file.xyz" }, - * ) - * ``` - * - convert your loaded file to equivalent javascript/typescript code using the {@link parseToJs} method. - * ```ts - * const js_content = await my_file_loader.parseToJs() - * ``` + * ## How the loader works: + * + * To put it simply, a subclass of {@link GenericLoader} performs the following steps in order: + * + * 1. {@link GenericLoader.extractDeps} parses the dependencies of the provided `content`. + * 2. {@link GenericLoader.parseToJs} creates a javascript-code that dynamically imports the dependencies, and exports the original `content`. + * 3. [**you**](https://en.wikipedia.org/wiki/human) pass the javacscript-code to `esbuild` for bundling and transformation of the import statements. + * 4. {@link GenericLoader.unparseFromJs} parses the resulting output javascript-code and extracts the new path names of the dependencies. + * 5. {@link GenericLoader.injectDeps} merges back the parsed dependencies to the original `content`. + * + * ## Loader usage example: + * + * Here is how you would typically use a subclass of the {@link GenericLoader}: + * + * - instantiate a {@link GenericLoader} instance with optional config (which currently does nothing). + * + * ```ts + * // make sure that you have extended `GenericLoader` and redefined the abstract methods + * class MyLoader extends GenericLoader {} + * + * const my_file_loader = new MyLoader({ + * path: "D:/my/project/my_file.xyz", + * }) + * ``` + * + * - convert the contents of the file you wish to bundle to equivalent javascript code using the {@link GenericLoader.parseToJs} method. + * + * ```ts + * const js_content = await my_file_loader.parseToJs() + * ``` + * * - pass the js content to your esbuild plugin's `onLoad` result, or use it as an entrypoint via `stdin`. - * ```ts - * const build_result = await esbuild.build({ - * absWorkingDir: "D:/my/project/", - * splitting: true, // required, so that the bundled `js_content` imports the referenced dependency files, instead of having them injected. - * format: "esm", // required for the `splitting` option to work - * bundle: true, // required, otherwise all links/dependencies will be treated as "external" and won't be transformed. - * outdir: "./out/", // required, for multiple output files - * write: false, // required, because the bundled content needs to exist in-memory for us to transform/unparse it back to its original form. - * minify: true, // optiotnal, useful for treeshaking. - * chunkNames: "[ext]/[name]-[hash]", // optional, useful for specifying the structure of the output directory - * assetNames: "assets/[name]-[hash]", // optional, useful for specifying the structure of the output directory - * plugins: [...denoPlugins()], // optional, use the Deno esbuild plugin to resolve "http://", "file://", "jsr:", and "npm:" imports. - * stdin: { - * contents: js_content, - * loader: "ts", - * resolveDir: "D:/my/project/", - * sourcefile: "D:/my/project/my_file.xyz", - * }, - * }) - * ``` - * - once the build is complete, convert back the bundled entrypoint from javascript to your file's format using the {@link unparseFromJs} method. - * ```ts - * const js_content_bundled = build_result.outputFiles[0].text // assuming that the first output file corresponds to your entrypoint - * const my_file_bundled = await my_file_loader.unparseFromJs(js_content_bundled) - * ``` - * - merge back the string contents of `my_file_bundled` to `build_results.outputFiles`, and then write the outputs to the filesystem using the {@link writeOutputFiles} utility function. - * ```ts - * const { hash, path } = outputs.outputFiles[0] - * build_result.outputFiles[0] = { text: my_file_bundled, hash, path } - * await writeOutputFiles(outputs.outputFiles) - * ``` + * + * ```ts + * const build_result = await esbuild.build({ + * absWorkingDir: "D:/my/project/", + * splitting: true, // required, so that the bundled `js_content` imports the referenced dependency files, instead of having them injected. + * format: "esm", // required for the `splitting` option to work + * bundle: true, // required, otherwise all links/dependencies will be treated as "external" and won't be transformed. + * outdir: "./out/", // required, for multiple output files + * write: false, // required, because the bundled content needs to exist in-memory for us to transform/unparse it back to its original form. + * minify: true, // optiotnal, useful for treeshaking. + * chunkNames: "[ext]/[name]-[hash]", // optional, useful for specifying the structure of the output directory + * assetNames: "assets/[name]-[hash]", // optional, useful for specifying the structure of the output directory + * plugins: [...denoPlugins()], // optional, use the Deno esbuild plugin to resolve "http://", "file://", "jsr:", and "npm:" imports. + * stdin: { + * contents: js_content, + * loader: "ts", + * resolveDir: "D:/my/project/", + * sourcefile: "D:/my/project/my_file.xyz", + * }, + * }) + * ``` + * + * - once the build is complete, convert back the bundled entrypoint from javascript to your file's format using the {@link GenericLoader.unparseFromJs} method. + * + * ```ts + * const js_content_bundled = build_result.outputFiles[0].text // assuming that the first output file corresponds to your entrypoint + * const my_file_bundled = await my_file_loader.unparseFromJs(js_content_bundled) + * ``` + * + * - merge back the string contents of `my_file_bundled` to `build_results.outputFiles`, + * and then write the outputs to the filesystem using the {@link "fs"!writeOutputFiles} utility function. + * + * ```ts + * const { hash, path } = outputs.outputFiles[0] + * build_result.outputFiles[0] = { text: my_file_bundled, hash, path } + * await writeOutputFiles(outputs.outputFiles) + * ``` * * @module */ @@ -81,8 +100,6 @@ await import(${json_stringify(import_path)})` /** the base class for creating custom loaders for any file type that is natively unsupported by `esbuild`. * - each loader _class_ handles one type of new file type. * - each loader _instance_ handles **one file**, and can be used only **once**, so that it does not hog onto resources. - * - * TODO: make to possible to enable string literal templating by removing the replacement of the dollarsign from `stringToJsEvalString` */ export abstract class GenericLoader { public meta: { imports: ImportMetadata } = { imports: [] } @@ -111,7 +128,7 @@ export abstract class GenericLoader { * * by default, the baseclass {@link GenericLoader} escapes all characters of the `content` parameter, * so that the string is perfectly preserved after the virtual module's evaluation.
- * this is achieved by using `String.raw` and escaping all dollarsigns ("$") and backticks ("\`") with template expressions. + * this is achieved by using `String.raw` and escaping all dollarsigns ("$") and backticks ("\\`") with template expressions. * however, such a thing may not be desirable, and you may want the evaluation of the template expressions within your `content`, rather than suppressing it. * or you may wish to introduce additional functions to the script so that it evaluates the output content through a series of transformations.
* in such cases, you would want to overload this method to suit your transformations needs. diff --git a/src/mod.ts b/src/mod.ts index 2a9d06d..00e18e4 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -2,3 +2,8 @@ * * A utility library for building generic file loading plugins for esbuild that are compatible with `Deno` runtime and its esbuild plugin ([`jsr:@luca/esbuild-deno-loader`](https://jsr.io/@luca/esbuild-deno-loader)). */ + +export * from "./funcdefs.ts" +export { GenericLoader } from "./loader.ts" +export type * from "./typedefs.ts" +