From 6df8c0375852764b0a4b796365a92db1267a0b00 Mon Sep 17 00:00:00 2001 From: Mayank Date: Wed, 25 Jun 2025 11:35:14 +0530 Subject: [PATCH 01/11] fix: update plugin name to a consistent identifier --- lib/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/index.ts b/lib/src/index.ts index 770fe871..4517afb5 100644 --- a/lib/src/index.ts +++ b/lib/src/index.ts @@ -30,7 +30,7 @@ export interface RawPluginOptions { * extensions in order of priority and handling custom loaders. */ export const raw: (options?: RawPluginOptions) => Plugin = options => ({ - name: `raw-${Math.random().toString(36).slice(2, 10)}`, + name: `esbuild-raw-plugin`, setup(build: PluginBuild) { const ext = options?.ext ?? [ "ts", From ff090cd55642c9110bed9258b8153a334645a58f Mon Sep 17 00:00:00 2001 From: Mayank Date: Wed, 25 Jun 2025 13:06:05 +0530 Subject: [PATCH 02/11] refactor: improve raw plugin file handling and error messages --- lib/src/index.ts | 94 ++++++++++++++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 35 deletions(-) diff --git a/lib/src/index.ts b/lib/src/index.ts index 4517afb5..2d9c6724 100644 --- a/lib/src/index.ts +++ b/lib/src/index.ts @@ -2,6 +2,19 @@ import type { Plugin, PluginBuild } from "esbuild"; import fs from "node:fs"; import path from "node:path"; +const DEFAULT_EXT_ORDER_LIST = [ + "ts", + "tsx", + "js", + "jsx", + "mjs", + "mts", + "module.css", + "module.scss", + "css", + "scss", +]; + export interface RawPluginOptions { /** * File extensions to check in order of priority if the specified file is missing. @@ -29,67 +42,78 @@ export interface RawPluginOptions { * treating them as raw text content. It supports resolving file * extensions in order of priority and handling custom loaders. */ -export const raw: (options?: RawPluginOptions) => Plugin = options => ({ - name: `esbuild-raw-plugin`, +export const raw = (options?: RawPluginOptions): Plugin => ({ + name: "esbuild-raw-plugin", setup(build: PluginBuild) { - const ext = options?.ext ?? [ - "ts", - "tsx", - "js", - "jsx", - "mjs", - "mts", - "module.css", - "module.scss", - "css", - "scss", - ]; + const ext = options?.ext ?? DEFAULT_EXT_ORDER_LIST; - build.onResolve({ filter: /\?raw$/ }, args => ({ - path: args.path, - pluginData: path.resolve(args.resolveDir, args.path).replace(/\?raw$/, ""), - namespace: "raw", - })); + build.onResolve({ filter: /\?(raw|text|buffer|binary|base64|dataurl|file)$/ }, args => { + const [filepath, query] = args.path.split("?"); + return { + path: filepath, + namespace: "raw", + pluginData: { + fullPath: path.resolve(args.resolveDir, filepath), + query, + }, + }; + }); - build.onLoad({ filter: /\?raw$/, namespace: "raw" }, args => { - let filePath = args.pluginData; - if (options?.loader && options.loader !== "text") { - return { contents: fs.readFileSync(filePath, "utf8"), loader: options.loader }; - } + build.onLoad({ filter: /.*/, namespace: "raw" }, args => { + let { fullPath, query } = args.pluginData; + let filePath = fullPath; if (fs.existsSync(filePath) && fs.lstatSync(filePath).isDirectory()) { filePath = path.join(filePath, "index"); } if (!fs.existsSync(filePath)) { - for (const e of ext) { - if (fs.existsSync(`${filePath}.${e}`)) { - filePath += `.${e}`; - break; - } + const resolved = ext + .map(e => e.replace(/^\./, "")) + .find(e => fs.existsSync(`${filePath}.${e}`)); + if (resolved) { + filePath += `.${resolved}`; } } if (!fs.existsSync(filePath)) { throw new Error( - /* v8 ignore next */ - `File not found: ${args.pluginData}\nChecked extensions: ${ext.join(", ")}. You can customize this using { ext: [...] }.`, - /* v8 ignore next */ + `File not found: ${fullPath}\nChecked extensions: ${ext.join(", ")}.\nYou can customize extensions list using { ext: [...] }.`, ); } - return { contents: fs.readFileSync(filePath, "utf8"), loader: "text" }; + const buffer = fs.readFileSync(filePath); + const suffix = query?.toLowerCase(); + + let loader = options?.loader ?? "text"; + switch (suffix) { + case "text": + loader = "text"; + break; + case "buffer": + case "binary": + loader = "binary"; + break; + case "file": + case "base64": + case "dataurl": + loader = suffix; + } + + return { contents: buffer, loader }; }); if (options?.textExtensions?.length) { build.onLoad( { filter: new RegExp( - `\.(${options.textExtensions.map(e => e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})$`, + `\\.(${options.textExtensions + .map(e => e.replace(/^\./, "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&")) + .join("|")})$`, ), }, args => ({ - contents: fs.readFileSync(args.path, "utf8"), + contents: fs.readFileSync(args.path), loader: "text", }), ); From f95ba3e80079d682b1e6e9a8afceb8b7015cb523 Mon Sep 17 00:00:00 2001 From: Mayank Date: Wed, 25 Jun 2025 13:08:43 +0530 Subject: [PATCH 03/11] refactor: enhance RawPluginOptions interface and improve loader handling --- lib/src/index.ts | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/lib/src/index.ts b/lib/src/index.ts index 2d9c6724..0c4206f6 100644 --- a/lib/src/index.ts +++ b/lib/src/index.ts @@ -25,6 +25,7 @@ export interface RawPluginOptions { /** * Custom loader for file processing. + * Overridden by import query suffix (?text, ?base64, etc). * @defaultValue "text" */ loader?: "text" | "base64" | "dataurl" | "file" | "binary" | "default"; @@ -33,6 +34,11 @@ export interface RawPluginOptions { * Extensions to be treated as text files. */ textExtensions?: string[]; + + /** + * Extension name in case you are using some other extension with conflicting names. + */ + name?: string; } /** @@ -43,12 +49,13 @@ export interface RawPluginOptions { * extensions in order of priority and handling custom loaders. */ export const raw = (options?: RawPluginOptions): Plugin => ({ - name: "esbuild-raw-plugin", + name: options?.name || "esbuild-raw-plugin", setup(build: PluginBuild) { - const ext = options?.ext ?? DEFAULT_EXT_ORDER_LIST; + const ext = options?.ext?.map(e => e.replace(/^\./, "")) ?? DEFAULT_EXT_ORDER_LIST; build.onResolve({ filter: /\?(raw|text|buffer|binary|base64|dataurl|file)$/ }, args => { - const [filepath, query] = args.path.split("?"); + const query = args.path.split("?").pop(); + const filepath = args.path.replace(new RegExp(`\\?${query}$`), ""); return { path: filepath, namespace: "raw", @@ -68,9 +75,7 @@ export const raw = (options?: RawPluginOptions): Plugin => ({ } if (!fs.existsSync(filePath)) { - const resolved = ext - .map(e => e.replace(/^\./, "")) - .find(e => fs.existsSync(`${filePath}.${e}`)); + const resolved = ext.find(e => fs.existsSync(`${filePath}.${e}`)); if (resolved) { filePath += `.${resolved}`; } @@ -87,17 +92,20 @@ export const raw = (options?: RawPluginOptions): Plugin => ({ let loader = options?.loader ?? "text"; switch (suffix) { - case "text": - loader = "text"; - break; case "buffer": case "binary": loader = "binary"; break; + case "text": case "file": case "base64": case "dataurl": loader = suffix; + break; + case "raw": + break; + default: + console.warn(`?${suffix} not supported. Falling back to ${loader}`); } return { contents: buffer, loader }; From 8bc581bf94a0e98fb5daf16462bae6b237844547 Mon Sep 17 00:00:00 2001 From: Mayank Date: Wed, 25 Jun 2025 13:50:24 +0530 Subject: [PATCH 04/11] refactor: update RawPluginOptions to use customLoaders and improve file handling --- lib/src/index.ts | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/lib/src/index.ts b/lib/src/index.ts index 0c4206f6..309cb3f2 100644 --- a/lib/src/index.ts +++ b/lib/src/index.ts @@ -31,12 +31,13 @@ export interface RawPluginOptions { loader?: "text" | "base64" | "dataurl" | "file" | "binary" | "default"; /** - * Extensions to be treated as text files. + * Map file extensions (without dot) to custom loaders. + * Example: { md: "text", png: "dataurl" } */ - textExtensions?: string[]; + customLoaders?: Record; /** - * Extension name in case you are using some other extension with conflicting names. + * Plugin name override (for debugging, deduplication, etc.) */ name?: string; } @@ -54,8 +55,10 @@ export const raw = (options?: RawPluginOptions): Plugin => ({ const ext = options?.ext?.map(e => e.replace(/^\./, "")) ?? DEFAULT_EXT_ORDER_LIST; build.onResolve({ filter: /\?(raw|text|buffer|binary|base64|dataurl|file)$/ }, args => { - const query = args.path.split("?").pop(); - const filepath = args.path.replace(new RegExp(`\\?${query}$`), ""); + const i = args.path.lastIndexOf("?"); + const filepath = i !== -1 ? args.path.slice(0, i) : args.path; + const query = i !== -1 ? args.path.slice(i + 1) : undefined; + return { path: filepath, namespace: "raw", @@ -111,20 +114,25 @@ export const raw = (options?: RawPluginOptions): Plugin => ({ return { contents: buffer, loader }; }); - if (options?.textExtensions?.length) { - build.onLoad( - { - filter: new RegExp( - `\\.(${options.textExtensions - .map(e => e.replace(/^\./, "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&")) - .join("|")})$`, - ), - }, - args => ({ - contents: fs.readFileSync(args.path), - loader: "text", - }), + if (options?.customLoaders) { + const customLoaderKeys = Object.keys(options.customLoaders).sort( + (a, b) => b.length - a.length, + ); + const pattern = new RegExp( + `\\.(${customLoaderKeys + .map(e => e.replace(/^\./, "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&")) + .join("|")})$`, ); + + build.onLoad({ filter: pattern }, args => { + const path = args.path; + const loaderKey = customLoaderKeys.find(suffix => path.endsWith(suffix)); + const loader = options.customLoaders?.[loaderKey ?? ""]; + if (!loader) return; + + const buffer = fs.readFileSync(path); + return { contents: buffer, loader }; + }); } }, }); From 4f4b532cbc339b07d0caccdcd1e904889f6ed208 Mon Sep 17 00:00:00 2001 From: Mayank Date: Wed, 25 Jun 2025 14:32:11 +0530 Subject: [PATCH 05/11] docs: enhance README with improved descriptions and formatting --- README.md | 206 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 108 insertions(+), 98 deletions(-) diff --git a/README.md b/README.md index 8122fad5..bb894675 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,45 @@ # Esbuild Raw Plugin -[![test](https://github.com/react18-tools/esbuild-raw-plugin/actions/workflows/test.yml/badge.svg)](https://github.com/react18-tools/esbuild-raw-plugin/actions/workflows/test.yml) [![Maintainability](https://api.codeclimate.com/v1/badges/aa896ec14c570f3bb274/maintainability)](https://codeclimate.com/github/react18-tools/esbuild-raw-plugin/maintainability) [![codecov](https://codecov.io/gh/react18-tools/esbuild-raw-plugin/graph/badge.svg)](https://codecov.io/gh/react18-tools/esbuild-raw-plugin) [![Version](https://img.shields.io/npm/v/esbuild-raw-plugin.svg?colorB=green)](https://www.npmjs.com/package/esbuild-raw-plugin) [![Downloads](https://img.jsdelivr.com/img.shields.io/npm/d18m/esbuild-raw-plugin.svg)](https://www.npmjs.com/package/esbuild-raw-plugin) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/esbuild-raw-plugin) [![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/from-referrer/) +[![test](https://github.com/react18-tools/esbuild-raw-plugin/actions/workflows/test.yml/badge.svg)](https://github.com/react18-tools/esbuild-raw-plugin/actions/workflows/test.yml) +[![Maintainability](https://api.codeclimate.com/v1/badges/aa896ec14c570f3bb274/maintainability)](https://codeclimate.com/github/react18-tools/esbuild-raw-plugin/maintainability) +[![codecov](https://codecov.io/gh/react18-tools/esbuild-raw-plugin/graph/badge.svg)](https://codecov.io/gh/react18-tools/esbuild-raw-plugin) +[![Version](https://img.shields.io/npm/v/esbuild-raw-plugin.svg?colorB=green)](https://www.npmjs.com/package/esbuild-raw-plugin) +[![Downloads](https://img.jsdelivr.com/img.shields.io/npm/d18m/esbuild-raw-plugin.svg)](https://www.npmjs.com/package/esbuild-raw-plugin) +![npm bundle size](https://img.shields.io/bundlephobia/minzip/esbuild-raw-plugin) +[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/from-referrer/) -**An ESBuild/TSUP plugin to import files as raw text.** -Ideal for scenarios like importing code files for documentation, interactive tools like `react-live`, or other text-based use cases. +**Lightweight ESBuild/TSUP plugin to import files as raw content β€” zero config required.** -> Star [this repository](https://github.com/react18-tools/esbuild-raw-plugin) and share it with your friends. +> Import `.ts`, `.js`, `.css`, `.scss`, `.md`, `.html`, `.docx`, and more β€” perfect for documentation, live editors (`react-live`), markdown tooling, or template-driven workflows. +> Power users: Load `.docx` templates directly for [mdast2docx](https://github.com/md2docx/mdast2docx). + +> Star [this repository](https://github.com/react18-tools/esbuild-raw-plugin) and share it with your dev circle. --- -## Features +## πŸš€ Features -- Import any file (e.g., `.js`, `.ts`, `.css`, etc.) as raw text. -- Works seamlessly with **ESBuild** and **TSUP**. -- Perfect for documentation generators, live code editors, and similar tools. +- πŸ”§ Supports `?raw`, `?text`, `?base64`, `?dataurl`, `?binary`, and `?file` query suffixes +- 🧠 Smart fallback to extensions like `.ts`, `.tsx`, `index.[ext]`, etc. +- πŸ” Custom loader mapping (e.g., `module.scss` β†’ `text`, `png` β†’ `dataurl`) +- ⚑ Ultra-fast using regex-based native `onLoad` filter (Go-native perf) +- πŸͺΆ Works seamlessly with both [Tsup](https://tsup.egoist.dev/) and [ESBuild](https://esbuild.github.io/) --- -## Installation - -Using npm: +## πŸ“¦ Installation ```bash npm install esbuild-raw-plugin --save-dev ``` -Using yarn: +_or_ ```bash yarn add esbuild-raw-plugin --dev ``` -Using pnpm: +_or_ ```bash pnpm add esbuild-raw-plugin --save-dev @@ -39,13 +47,11 @@ pnpm add esbuild-raw-plugin --save-dev --- -## Usage +## πŸ›  Usage -### ESBuild Configuration +### ➀ With ESBuild -Add the plugin to your ESBuild configuration: - -```js +```ts import { build } from "esbuild"; import { raw } from "esbuild-raw-plugin"; @@ -57,11 +63,9 @@ build({ }); ``` -### TSUP Configuration +### ➀ With TSUP -Add the plugin to your TSUP configuration: - -```js +```ts import { defineConfig } from "tsup"; import { raw } from "esbuild-raw-plugin"; @@ -74,135 +78,141 @@ export default defineConfig({ --- -## IDE Setup for IntelliSense and Type Checking +## 🧠 TypeScript Support -Add the following to your declaration file. If you do not have one, create a `declarations.d.ts` file and add the following: +Add this to your `declarations.d.ts` file: -```typescript +```ts declare module "*?raw" { - const value: string; - export default value; + const content: string; + export default content; } ``` -## Importing Files as Raw Text +> For other suffixes (`?base64`, `?binary`, etc.), add similar declarations if needed. + +--- -With the plugin enabled, you can import files as raw text directly: +## πŸ“₯ Importing Raw Files -```js -import myCode from "./example.js?raw"; +```ts +import content from "./example.js?raw"; -console.log(myCode); -// Outputs the content of 'example.js' as a string. +console.log(content); // Entire file content as string or Buffer ``` -### Good News: +### βœ… Simplified Imports -With the latest update, you no longer need to specify the file extension explicitly. +You don’t need to specify full filenames or extensions: -```js -import myCode from "./example?raw"; +```ts +import code from "./utils?raw"; // Resolves to utils/index.ts, utils.js, etc. ``` -This works seamlessly! Additionally, if you're exporting from files like `index.tsx`, `index.jsx`, etc., you can simplify imports. For example, if your file path is `my-lib/index.ts`, you can import the raw content like this: +Great for: -```js -import myCode from "./my-lib?raw"; -``` +- Library or folder-level imports +- Auto-resolving `.ts`, `.tsx`, `.css`, `.scss`, etc. -### Extension Options (Optional) +--- + +## βš™οΈ Plugin Options ```ts export interface RawPluginOptions { - /** - * Extensions to check in order if the file does not exist. - * If it's a directory, the plugin will look for `dir/index.[ext]`. - * @defaultValue ["tsx", "ts", "jsx", "js", "mjs", "mts", "module.css", "module.scss", "css", "scss"] - * - * You can provide your own extensions to optimize build performance or extend the list based on your use case. - */ ext?: string[]; - - /** - * Custom loader for file processing. - * @defaultValue "text" - */ loader?: "text" | "base64" | "dataurl" | "file" | "binary" | "default"; - - /** - * Extensions to be treated as text files. - */ - textExtensions?: string[]; + customLoaders?: Record; + name?: string; } ``` -### Supported File Types +
+πŸ”§ Option Details -You can use `?raw` with any file type, including: +- `ext`: Extensions to resolve if the file or folder is missing. Defaults to common types like `ts`, `tsx`, `module.css`, etc. +- `loader`: Default loader if no `?query` is specified. Usually `"text"`. +- `customLoaders`: Per-extension loader mapping. Example: -- `.js`, `.ts`, `.jsx`, `.tsx` -- `.css`, `.scss` -- `.html` -- `.md` -- and more! + ```ts + { + "module.scss": "text", + "png": "dataurl", + "docx": "file" + } + ``` ---- +- `name`: Optional plugin name override for debugging or deduplication. -## Example Use Case +
-### Live Code Preview with `react-live` +--- -```jsx -import React from "react"; -import { LiveProvider, LiveEditor, LiveError, LivePreview } from "react-live"; -import exampleCode from "./example.js?raw"; +## πŸ§ͺ Supported Query Loaders -const App = () => ( - - - - - -); +Import with query-based syntax: -export default App; +```ts +import doc from "./readme.md?text"; +import logo from "./logo.png?base64"; +import wasm from "./core.wasm?binary"; ``` ---- - -## Why Use `esbuild-raw-plugin`? - -- Simplifies importing files as raw text for documentation and live previews. -- Seamlessly integrates with modern build tools like ESBuild and TSUP. -- Lightweight and easy to configure. +| Query Suffix | Description | +| ------------ | -------------------------------------------------- | +| `?raw` | Uses the default loader (options.loader ?? "text") | +| `?text` | Loads file as UTF-8 text | +| `?base64` | Returns base64 string | +| `?dataurl` | Returns full data URL | +| `?file` | Emits file to output dir | +| `?binary` | Returns raw `Buffer` | --- -## Keywords +## 🧬 Use Case: Live Code Preview -`esbuild`, `esbuild-plugin`, `tsup-plugin`, `raw-text-import`, `import-as-text`, `file-loader`, `react-live`, `documentation-tools`, `frontend-tooling` +```tsx +import { LiveProvider, LiveEditor, LiveError, LivePreview } from "react-live"; +import exampleCode from "./example.js?raw"; + +export default function LiveDemo() { + return ( + + + + + + ); +} +``` --- -## Contributing +## πŸ” Why Choose `esbuild-raw-plugin`? -Contributions are welcome! -Feel free to open issues or pull requests to improve the plugin. +- βœ… Works out of the box β€” no config needed +- πŸ“ Handles smart file resolution +- πŸ’¬ Excellent developer experience +- 🧩 Supports both query-based and extension-based mappings +- πŸ§ͺ Stable, fast, and production-tested --- -Let me know if you'd like further tweaks! πŸš€ +## πŸ›  Contributing + +PRs and ideas welcome! +Open an issue or submit a pull request to help improve the plugin. ![Alt](https://repobeats.axiom.co/api/embed/1ae166ef108b33b36ceaa60be208a5dafce25c5c.svg "Repobeats analytics image") --- -## License +## 🧾 License -This library is licensed under the MPL-2.0 open-source license. +Licensed under the **MPL-2.0** open-source license. -> Please enroll in [our courses](https://mayank-chaudhari.vercel.app/courses) or [sponsor](https://github.com/sponsors/mayank1513) our work. +> Please consider [sponsoring](https://github.com/sponsors/mayank1513) or [joining a course](https://mayank-chaudhari.vercel.app/courses) to support this work. -
+---

with πŸ’– by Mayank Kumar Chaudhari

From a89933605be940cc242f82e91707697cccf7a982 Mon Sep 17 00:00:00 2001 From: Mayank Date: Wed, 25 Jun 2025 15:11:45 +0530 Subject: [PATCH 06/11] refactor: replace textExtensions with customLoaders and enhance test cases --- .changeset/upgrade.md | 17 +++++++++++++++++ .gitignore | 2 +- lib/__tests__/index.test.ts | 36 +++++++++++++++++++++++++++++++++--- 3 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 .changeset/upgrade.md diff --git a/.changeset/upgrade.md b/.changeset/upgrade.md new file mode 100644 index 00000000..1804134c --- /dev/null +++ b/.changeset/upgrade.md @@ -0,0 +1,17 @@ +--- +esbuild-raw-plugin: minor +--- + +### ✨ Enhancements + +- Replaced `textExtensions` with `customLoaders` for fine-grained extension-to-loader mapping. +- Introduced `name` option for overriding the plugin name (useful for debugging or deduplication). +- Added support for multiple query-based loaders: `?text`, `?base64`, `?dataurl`, `?file`, `?binary`. +- Improved fallback logic for resolving files: now tries extensions or `index.[ext]` for folders. +- Regex-based `onLoad` filtering boosts performance (leveraging Go-native ESBuild internals). + +### πŸ›  Internal Refactors + +- Code refactored for better readability and maintainability. +- Error messages are now clearer and more actionable. +- Switched to consistent plugin naming (`"esbuild-raw-plugin"` instead of randomized suffix). diff --git a/.gitignore b/.gitignore index 544fb870..3503063f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ node_modules .turbo *.log .next -dist +dist* dist-ssr *.local .env diff --git a/lib/__tests__/index.test.ts b/lib/__tests__/index.test.ts index 640fd757..75424289 100644 --- a/lib/__tests__/index.test.ts +++ b/lib/__tests__/index.test.ts @@ -46,7 +46,7 @@ describe("Raw plugin", () => { test("textExtensions", async ({ expect }) => { await esbuild.build({ ...buildOptions, - plugins: [raw({ textExtensions: [".md"] })], + plugins: [raw({ customLoaders: { md: "text" } })], entryPoints: [path.resolve(__dirname, "test3.ts")], }); @@ -56,15 +56,45 @@ describe("Raw plugin", () => { expect(fileContent).toBe(generatedCodeContent); }); + test("uses custom loader if provided", async ({ expect }) => { + await esbuild.build({ + ...buildOptions, + entryPoints: [path.resolve(__dirname, "test-loader.ts")], + plugins: [raw({ loader: "base64" })], + outdir: "__tests__/dist2", + }); + const fileContent = fs.readFileSync(path.resolve(__dirname, "../src/index.ts")); + // @ts-ignore + const generatedCodeContent = (await import("./dist2/test-loader.js")).getText(); + expect(generatedCodeContent).toBe(fileContent.toString("base64")); + }); + + test("uses customLoaders mapping for extension", async ({ expect }) => { + await esbuild.build({ + ...buildOptions, + plugins: [raw({ customLoaders: { md: "text" } })], + entryPoints: [path.resolve(__dirname, "test3.ts")], + outdir: "__tests__/dist3", + }); + const fileContent = fs.readFileSync(path.resolve(__dirname, "test.md"), "utf-8"); + // @ts-ignore + const generatedCodeContent = (await import("./dist3/test3.js")).getText(); + expect(generatedCodeContent).toBe(fileContent); + }); + + test("uses custom plugin name if provided", ({ expect }) => { + const plugin = raw({ name: "custom-plugin-name" }); + expect(plugin.name).toBe("custom-plugin-name"); + }); test("custom loader", async ({ expect }) => { await esbuild.build({ ...buildOptions, entryPoints: [path.resolve(__dirname, "test-loader.ts")], plugins: [raw({ loader: "base64" })], }); - const fileContent = fs.readFileSync(path.resolve(__dirname, "../src/index.ts"), "utf-8"); + const fileContent = fs.readFileSync(path.resolve(__dirname, "../src/index.ts")); // @ts-ignore const generatedCodeContent = (await import("./dist/test-loader.js")).getText(); - expect(fileContent).toBe(atob(generatedCodeContent)); + expect(generatedCodeContent).toBe(fileContent.toString("base64")); }); }); From 8463ce8d79e3a030fa5ecb5a075064e9c68b2895 Mon Sep 17 00:00:00 2001 From: Mayank Date: Wed, 25 Jun 2025 15:15:21 +0530 Subject: [PATCH 07/11] refactor: update module declarations and test cases to support base64 imports --- lib/__tests__/declarations.d.ts | 5 +++++ lib/__tests__/index.test.ts | 4 ++-- lib/__tests__/test1.ts | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/__tests__/declarations.d.ts b/lib/__tests__/declarations.d.ts index 7adcba24..9800d7e6 100644 --- a/lib/__tests__/declarations.d.ts +++ b/lib/__tests__/declarations.d.ts @@ -1,2 +1,7 @@ +declare module "*?file"; +declare module "*?base64"; +declare module "*?binary"; +declare module "*?buffer"; +declare module "*?text"; declare module "*?raw"; declare module "*.md"; diff --git a/lib/__tests__/index.test.ts b/lib/__tests__/index.test.ts index 75424289..8cb9de66 100644 --- a/lib/__tests__/index.test.ts +++ b/lib/__tests__/index.test.ts @@ -27,10 +27,10 @@ describe("Raw plugin", () => { test("test raw import with auto ext", async ({ expect }) => { await esbuild.build({ ...buildOptions, entryPoints: [path.resolve(__dirname, "test1.ts")] }); - const fileContent = fs.readFileSync(path.resolve(__dirname, "../src/index.ts"), "utf-8"); + const fileContent = fs.readFileSync(path.resolve(__dirname, "../src/index.ts")); // @ts-ignore const generatedCodeContent = (await import("./dist/test1.js")).getText(); - expect(fileContent).toBe(generatedCodeContent); + expect(generatedCodeContent).toBe(fileContent.toString("base64")); }); test("throws error if no file is found", async ({ expect }) => { diff --git a/lib/__tests__/test1.ts b/lib/__tests__/test1.ts index 4f2e2232..87a99ad2 100644 --- a/lib/__tests__/test1.ts +++ b/lib/__tests__/test1.ts @@ -1,5 +1,5 @@ // test auto complete -import text from "../src?raw"; +import text from "../src?base64"; export const getText = () => text; From a60fee4852e5f4f02042f92def0e625d9025e861 Mon Sep 17 00:00:00 2001 From: Mayank Date: Wed, 25 Jun 2025 17:46:40 +0530 Subject: [PATCH 08/11] Remove unreachable default statement --- lib/src/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/src/index.ts b/lib/src/index.ts index 309cb3f2..c0c62611 100644 --- a/lib/src/index.ts +++ b/lib/src/index.ts @@ -107,8 +107,6 @@ export const raw = (options?: RawPluginOptions): Plugin => ({ break; case "raw": break; - default: - console.warn(`?${suffix} not supported. Falling back to ${loader}`); } return { contents: buffer, loader }; From d5f9ed827ad2fa351012903553ad9fcd12a3a2bf Mon Sep 17 00:00:00 2001 From: Mayank Date: Wed, 25 Jun 2025 17:47:06 +0530 Subject: [PATCH 09/11] test: enhance tests to include binary import handling and update error case --- lib/__tests__/index.test.ts | 3 +++ lib/__tests__/test1.ts | 2 ++ lib/__tests__/test2.ts | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/__tests__/index.test.ts b/lib/__tests__/index.test.ts index 8cb9de66..99ecb8e8 100644 --- a/lib/__tests__/index.test.ts +++ b/lib/__tests__/index.test.ts @@ -30,7 +30,10 @@ describe("Raw plugin", () => { const fileContent = fs.readFileSync(path.resolve(__dirname, "../src/index.ts")); // @ts-ignore const generatedCodeContent = (await import("./dist/test1.js")).getText(); + // @ts-ignore + const generatedCodeContent2 = (await import("./dist/test1.js")).getText2(); expect(generatedCodeContent).toBe(fileContent.toString("base64")); + expect(generatedCodeContent2.toString("base64")).toBe(fileContent.toString("base64")); }); test("throws error if no file is found", async ({ expect }) => { diff --git a/lib/__tests__/test1.ts b/lib/__tests__/test1.ts index 87a99ad2..4555ee28 100644 --- a/lib/__tests__/test1.ts +++ b/lib/__tests__/test1.ts @@ -1,5 +1,7 @@ // test auto complete import text from "../src?base64"; +import text2 from "../src?binary"; export const getText = () => text; +export const getText2 = () => text2; diff --git a/lib/__tests__/test2.ts b/lib/__tests__/test2.ts index ad640664..aa3e32a3 100644 --- a/lib/__tests__/test2.ts +++ b/lib/__tests__/test2.ts @@ -1,5 +1,5 @@ // test auto error -import text from "../src/my-file?raw"; +import text from "../src/my-file?buffer"; export const getText = () => text; From 811d3f401b035252a1b1b631438b3edeede80d47 Mon Sep 17 00:00:00 2001 From: Mayank Date: Wed, 25 Jun 2025 17:55:50 +0530 Subject: [PATCH 10/11] test: add buffer handling test and implement getBuffer function --- lib/__tests__/index.test.ts | 11 +++++++++++ lib/__tests__/test4.ts | 5 +++++ 2 files changed, 16 insertions(+) create mode 100644 lib/__tests__/test4.ts diff --git a/lib/__tests__/index.test.ts b/lib/__tests__/index.test.ts index 99ecb8e8..805e85ea 100644 --- a/lib/__tests__/index.test.ts +++ b/lib/__tests__/index.test.ts @@ -36,6 +36,17 @@ describe("Raw plugin", () => { expect(generatedCodeContent2.toString("base64")).toBe(fileContent.toString("base64")); }); + test("test buffer", async ({ expect }) => { + await esbuild.build({ ...buildOptions, entryPoints: [path.resolve(__dirname, "test4.ts")] }); + const fileContent = fs.readFileSync(path.resolve(__dirname, "../src/index.ts")); + // @ts-ignore + const generatedCodeContent = (await import("./dist/test4.js")).getBuffer(); + // @ts-ignore + expect(Buffer.from(generatedCodeContent).toString("base64")).toBe( + fileContent.toString("base64"), + ); + }); + test("throws error if no file is found", async ({ expect }) => { let didThrow = false; try { diff --git a/lib/__tests__/test4.ts b/lib/__tests__/test4.ts new file mode 100644 index 00000000..44a3afde --- /dev/null +++ b/lib/__tests__/test4.ts @@ -0,0 +1,5 @@ +// test auto complete + +import buffer from "../src?binary"; + +export const getBuffer = () => buffer; From 219a90ac9219be0ea9bbfd5f177646649a96ab55 Mon Sep 17 00:00:00 2001 From: Mayank Date: Wed, 25 Jun 2025 17:59:41 +0530 Subject: [PATCH 11/11] refactor: use const for destructured pluginData in onLoad handler --- lib/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/index.ts b/lib/src/index.ts index c0c62611..592f21b3 100644 --- a/lib/src/index.ts +++ b/lib/src/index.ts @@ -70,7 +70,7 @@ export const raw = (options?: RawPluginOptions): Plugin => ({ }); build.onLoad({ filter: /.*/, namespace: "raw" }, args => { - let { fullPath, query } = args.pluginData; + const { fullPath, query } = args.pluginData; let filePath = fullPath; if (fs.existsSync(filePath) && fs.lstatSync(filePath).isDirectory()) {