-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.ts
More file actions
154 lines (132 loc) · 4.3 KB
/
index.ts
File metadata and controls
154 lines (132 loc) · 4.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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.
* If it's a directory, the plugin will look for `dir/index.[ext]`.
* @defaultValue ["ts", "tsx", "js", "jsx", "mjs", "mts", "module.css", "module.scss", "css", "scss"]
*/
ext?: string[];
/**
* Custom loader for file processing.
* Overridden by import query suffix (?text, ?base64, etc).
* @defaultValue "text"
*/
loader?: "text" | "base64" | "dataurl" | "file" | "binary" | "default";
/**
* Map file extensions (without dot) to custom loaders.
* Example: { md: "text", png: "dataurl" }
*/
customLoaders?: Record<string, "text" | "base64" | "dataurl" | "file" | "binary" | "default">;
/**
* Plugin name override (for debugging, deduplication, etc.)
*/
name?: string;
/**
* @deprecated Use `customLoaders` instead.
* Previously used to specify extensions to treat as text.
*
* Example replacement:
* ```ts
* customLoaders: { "module.scss": "text", "md": "text" }
* ```
*/
textExtensions?: string[];
}
/**
* ESBuild plugin to enable raw file imports.
*
* This plugin allows importing files with a `?raw` suffix,
* 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 => ({
name: options?.name || "esbuild-raw-plugin",
setup(build: PluginBuild) {
const ext = options?.ext?.map(e => e.replace(/^\./, "")) ?? DEFAULT_EXT_ORDER_LIST;
build.onResolve({ filter: /\?(raw|text|buffer|binary|base64|dataurl|file)$/ }, args => {
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",
pluginData: {
fullPath: path.resolve(args.resolveDir, filepath),
query,
},
};
});
build.onLoad({ filter: /.*/, namespace: "raw" }, args => {
const { fullPath, query } = args.pluginData;
let filePath = fullPath;
if (fs.existsSync(filePath) && fs.lstatSync(filePath).isDirectory()) {
filePath = path.join(filePath, "index");
}
if (!fs.existsSync(filePath)) {
const resolved = ext.find(e => fs.existsSync(`${filePath}.${e}`));
if (resolved) {
filePath += `.${resolved}`;
}
}
if (!fs.existsSync(filePath)) {
throw new Error(
`File not found: ${fullPath}\nChecked extensions: ${ext.join(", ")}.\nYou can customize extensions list using { ext: [...] }.`,
);
}
const buffer = fs.readFileSync(filePath);
const suffix = query?.toLowerCase();
let loader = options?.loader ?? "text";
switch (suffix) {
case "buffer":
case "binary":
loader = "binary";
break;
case "text":
case "file":
case "base64":
case "dataurl":
loader = suffix;
break;
case "raw":
break;
}
return { contents: buffer, loader };
});
if (options?.customLoaders || options?.textExtensions) {
const customLoaderKeys = [
...new Set([
...Object.keys(options.customLoaders ?? {}),
...(options?.textExtensions ?? []),
]),
].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));
let loader = options.customLoaders?.[loaderKey ?? ""];
if (!loader && options?.textExtensions?.includes(loaderKey ?? "")) loader = "text";
if (!loader) return;
const buffer = fs.readFileSync(path);
return { contents: buffer, loader };
});
}
},
});