aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Colin McDonnell <colinmcd94@gmail.com> 2023-06-06 23:05:13 -0700
committerGravatar Colin McDonnell <colinmcd94@gmail.com> 2023-06-06 23:05:13 -0700
commit599162ce73c29eccc1bb5375a25dc748b6cb1973 (patch)
tree62410b7df269001c919d78f62dd8cd6b5a35d75e
parentec71e7afe49d5110f3c9d0eba8e49ea22a549e41 (diff)
downloadbun-599162ce73c29eccc1bb5375a25dc748b6cb1973.tar.gz
bun-599162ce73c29eccc1bb5375a25dc748b6cb1973.tar.zst
bun-599162ce73c29eccc1bb5375a25dc748b6cb1973.zip
Add resolveDir and tests
-rw-r--r--.vscode/settings.json2
-rwxr-xr-xbun.lockbbin63917 -> 63660 bytes
-rw-r--r--packages/bun-types/bun.d.ts5
-rw-r--r--src/bun.js/api/JSBundler.zig7
-rw-r--r--src/bun.js/bindings/JSBundlerPlugin.cpp4
-rw-r--r--src/bundler/bundle_v2.zig3
-rw-r--r--src/js/builtins/BundlerPlugin.ts29
-rw-r--r--src/js/out/WebCoreJSBuiltins.cpp6
-rw-r--r--src/js/out/WebCoreJSBuiltins.h2
-rwxr-xr-xtest/bun.lockbbin36614 -> 51098 bytes
-rw-r--r--test/bundler/bundler_plugin.test.ts28
-rw-r--r--test/bundler/expectBundled.ts4
12 files changed, 75 insertions, 15 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 454cd849d..927e78510 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -2,7 +2,7 @@
"git.autoRepositoryDetection": "openEditors",
"search.quickOpen.includeSymbols": false,
"search.seedWithNearestWord": true,
- "search.smartCase": true,
+ "search.smartCase": false,
"search.exclude": {},
"search.followSymlinks": false,
"search.useIgnoreFiles": true,
diff --git a/bun.lockb b/bun.lockb
index 8e51bf3e4..93e50b7ae 100755
--- a/bun.lockb
+++ b/bun.lockb
Binary files differ
diff --git a/packages/bun-types/bun.d.ts b/packages/bun-types/bun.d.ts
index 2396ffbd9..633570909 100644
--- a/packages/bun-types/bun.d.ts
+++ b/packages/bun-types/bun.d.ts
@@ -2937,7 +2937,10 @@ declare module "bun" {
* The kind of import this resolve is for.
*/
kind: ImportKind;
- // resolveDir: string;
+ /**
+ * The= directory of the importing module. Commonly used to resolve `args.path` to an absolute path.
+ */
+ resolveDir: string;
// pluginData: any;
}
diff --git a/src/bun.js/api/JSBundler.zig b/src/bun.js/api/JSBundler.zig
index 8e85f1190..888e94b1f 100644
--- a/src/bun.js/api/JSBundler.zig
+++ b/src/bun.js/api/JSBundler.zig
@@ -893,6 +893,7 @@ pub const JSBundler = struct {
importer: *const ZigString,
context: *anyopaque,
u8,
+ resolve_dir: *const ZigString,
) void;
pub fn hasAnyMatches(
@@ -949,7 +950,11 @@ pub const JSBundler = struct {
ZigString.fromUTF8(namespace);
const path_string = ZigString.fromUTF8(path);
const importer_string = ZigString.fromUTF8(importer);
- JSBundlerPlugin__matchOnResolve(globalThis, this, &namespace_string, &path_string, &importer_string, context, @enumToInt(import_record_kind));
+ // TODO: improve this for virtual modules
+ const resolve_dir = std.fs.path.dirname(importer) orelse "/";
+ std.debug.print("{s}\n", .{resolve_dir});
+ const resolve_dir_string = ZigString.fromUTF8(resolve_dir);
+ JSBundlerPlugin__matchOnResolve(globalThis, this, &namespace_string, &path_string, &importer_string, context, @enumToInt(import_record_kind), &resolve_dir_string);
}
pub fn addPlugin(
diff --git a/src/bun.js/bindings/JSBundlerPlugin.cpp b/src/bun.js/bindings/JSBundlerPlugin.cpp
index e93556963..de936ab05 100644
--- a/src/bun.js/bindings/JSBundlerPlugin.cpp
+++ b/src/bun.js/bindings/JSBundlerPlugin.cpp
@@ -315,7 +315,7 @@ extern "C" void JSBundlerPlugin__matchOnLoad(JSC::JSGlobalObject* globalObject,
}
}
-extern "C" void JSBundlerPlugin__matchOnResolve(JSC::JSGlobalObject* globalObject, Bun::JSBundlerPlugin* plugin, const ZigString* namespaceString, const ZigString* path, const ZigString* importer, void* context, uint8_t kindId)
+extern "C" void JSBundlerPlugin__matchOnResolve(JSC::JSGlobalObject* globalObject, Bun::JSBundlerPlugin* plugin, const ZigString* namespaceString, const ZigString* path, const ZigString* importer, void* context, uint8_t kindId, const ZigString* resolveDir)
{
WTF::String namespaceStringStr = namespaceString ? Zig::toStringCopy(*namespaceString) : WTF::String("file"_s);
if (namespaceStringStr.length() == 0) {
@@ -323,6 +323,7 @@ extern "C" void JSBundlerPlugin__matchOnResolve(JSC::JSGlobalObject* globalObjec
}
WTF::String pathStr = path ? Zig::toStringCopy(*path) : WTF::String();
WTF::String importerStr = importer ? Zig::toStringCopy(*importer) : WTF::String();
+ WTF::String resolveDirStr = resolveDir ? Zig::toStringCopy(*resolveDir) : WTF::String();
auto& vm = globalObject->vm();
JSFunction* function = plugin->onResolveFunction.get(plugin);
@@ -341,6 +342,7 @@ extern "C" void JSBundlerPlugin__matchOnResolve(JSC::JSGlobalObject* globalObjec
arguments.append(JSC::jsString(vm, importerStr));
arguments.append(WRAP_BUNDLER_PLUGIN(context));
arguments.append(JSC::jsNumber(kindId));
+ arguments.append(JSC::jsString(vm, resolveDirStr));
auto result = call(globalObject, function, callData, plugin, arguments);
diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig
index c62be6153..bdc621bab 100644
--- a/src/bundler/bundle_v2.zig
+++ b/src/bundler/bundle_v2.zig
@@ -1352,8 +1352,11 @@ pub const BundleV2 = struct {
_ = @atomicRmw(usize, &this.graph.parse_pending, .Sub, 1, .Monotonic);
},
.success => |code| {
+
this.graph.input_files.items(.loader)[load.source_index.get()] = code.loader;
this.graph.input_files.items(.source)[load.source_index.get()].contents = code.source_code;
+ // print source code
+ debug("source code: {s}", .{ code.source_code });
var parse_task = load.parse_task;
parse_task.loader = code.loader;
this.free_list.append(code.source_code) catch unreachable;
diff --git a/src/js/builtins/BundlerPlugin.ts b/src/js/builtins/BundlerPlugin.ts
index 831a6614e..36dfba61b 100644
--- a/src/js/builtins/BundlerPlugin.ts
+++ b/src/js/builtins/BundlerPlugin.ts
@@ -189,11 +189,19 @@ export function runSetupFunction(this: BundlerPlugin, setup: Setup, config: Buil
return processSetupResult();
}
-export function runOnResolvePlugins(this: BundlerPlugin, specifier, inputNamespace, importer, internalID, kindId) {
+export function runOnResolvePlugins(
+ this: BundlerPlugin,
+ specifier,
+ inputNamespace,
+ importer,
+ internalID,
+ kindId,
+ resolveDir,
+) {
// Must be kept in sync with ImportRecord.label
const kind = $ImportKindIdToLabel[kindId];
- var promiseResult: any = (async (inputPath, inputNamespace, importer, kind) => {
+ var promiseResult: any = (async (inputPath, inputNamespace, importer, kind, resolveDir) => {
var { onResolve, onLoad } = this;
var results = onResolve.$get(inputNamespace);
if (!results) {
@@ -207,7 +215,7 @@ export function runOnResolvePlugins(this: BundlerPlugin, specifier, inputNamespa
path: inputPath,
importer,
namespace: inputNamespace,
- // resolveDir
+ resolveDir,
kind,
// pluginData
});
@@ -229,8 +237,12 @@ export function runOnResolvePlugins(this: BundlerPlugin, specifier, inputNamespa
}
var { path, namespace: userNamespace = inputNamespace, external } = result;
- if (!(typeof path === "string") || !(typeof userNamespace === "string")) {
- throw new TypeError("onResolve plugins must return an object with a string 'path' and string 'loader' field");
+ if (!(typeof path === "string")) {
+ throw new TypeError("onResolve: expected 'path' to be a string");
+ }
+
+ if (!(typeof userNamespace === "string")) {
+ throw new TypeError("onResolve: expected 'namespace' to be a string");
}
if (!path) {
@@ -241,7 +253,7 @@ export function runOnResolvePlugins(this: BundlerPlugin, specifier, inputNamespa
userNamespace = inputNamespace;
}
if (typeof external !== "boolean" && !$isUndefinedOrNull(external)) {
- throw new TypeError('onResolve plugins "external" field must be boolean or unspecified');
+ throw new TypeError("onResolve: expected 'external' to be boolean");
}
if (!external) {
@@ -271,7 +283,7 @@ export function runOnResolvePlugins(this: BundlerPlugin, specifier, inputNamespa
this.onResolveAsync(internalID, null, null, null);
return null;
- })(specifier, inputNamespace, importer, kind);
+ })(specifier, inputNamespace, importer, kind, resolveDir);
while (
promiseResult &&
@@ -329,6 +341,9 @@ export function runOnLoadPlugins(this: BundlerPlugin, internalID, path, namespac
}
var { contents, loader = defaultLoader } = result as OnLoadResultSourceCode & OnLoadResultObject;
+
+ // TODO: Support "object" loader
+
if (!(typeof contents === "string") && !$isTypedArrayView(contents)) {
throw new TypeError('onLoad plugins must return an object with "contents" as a string or Uint8Array');
}
diff --git a/src/js/out/WebCoreJSBuiltins.cpp b/src/js/out/WebCoreJSBuiltins.cpp
index b57e346b5..4a35b04ee 100644
--- a/src/js/out/WebCoreJSBuiltins.cpp
+++ b/src/js/out/WebCoreJSBuiltins.cpp
@@ -22,9 +22,9 @@ const char* const s_bundlerPluginRunSetupFunctionCode = "(function (_,E){\"use s
const JSC::ConstructAbility s_bundlerPluginRunOnResolvePluginsCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
const JSC::ConstructorKind s_bundlerPluginRunOnResolvePluginsCodeConstructorKind = JSC::ConstructorKind::None;
const JSC::ImplementationVisibility s_bundlerPluginRunOnResolvePluginsCodeImplementationVisibility = JSC::ImplementationVisibility::Public;
-const int s_bundlerPluginRunOnResolvePluginsCodeLength = 1711;
+const int s_bundlerPluginRunOnResolvePluginsCodeLength = 1732;
static const JSC::Intrinsic s_bundlerPluginRunOnResolvePluginsCodeIntrinsic = JSC::NoIntrinsic;
-const char* const s_bundlerPluginRunOnResolvePluginsCode = "(function (_,w,g,y,b){\"use strict\";const j=[\"entry-point\",\"import-statement\",\"require-call\",\"dynamic-import\",\"require-resolve\",\"import-rule\",\"url-token\",\"internal\"][b];var q=(async(z,A,B,C)=>{var{onResolve:E,onLoad:F}=this,G=E.@get(A);if(!G)return this.onResolveAsync(y,null,null,null),null;for(let[O,Q]of G)if(O.test(z)){var H=Q({path:z,importer:B,namespace:A,kind:C});while(H&&@isPromise(H)&&(@getPromiseInternalField(H,@promiseFieldFlags)&@promiseStateMask)===@promiseStateFulfilled)H=@getPromiseInternalField(H,@promiseFieldReactionsOrResult);if(H&&@isPromise(H))H=await H;if(!H||!@isObject(H))continue;var{path:J,namespace:K=A,external:M}=H;if(typeof J!==\"string\"||typeof K!==\"string\")@throwTypeError(\"onResolve plugins must return an object with a string 'path' and string 'loader' field\");if(!J)continue;if(!K)K=A;if(typeof M!==\"boolean\"&&!@isUndefinedOrNull(M))@throwTypeError('onResolve plugins \"external\" field must be boolean or unspecified');if(!M){if(K===\"file\"){if(darwin!==\"win32\"){if(J[0]!==\"/\"||J.includes(\"..\"))@throwTypeError('onResolve plugin \"path\" must be absolute when the namespace is \"file\"')}}if(K===\"dataurl\"){if(!J.startsWith(\"data:\"))@throwTypeError('onResolve plugin \"path\" must start with \"data:\" when the namespace is \"dataurl\"')}if(K&&K!==\"file\"&&(!F||!F.@has(K)))@throwTypeError(`Expected onLoad plugin for namespace ${K} to exist`)}return this.onResolveAsync(y,J,K,M),null}return this.onResolveAsync(y,null,null,null),null})(_,w,g,j);while(q&&@isPromise(q)&&(@getPromiseInternalField(q,@promiseFieldFlags)&@promiseStateMask)===@promiseStateFulfilled)q=@getPromiseInternalField(q,@promiseFieldReactionsOrResult);if(q&&@isPromise(q))q.then(()=>{},(z)=>{this.addError(y,z,0)})})\n";
+const char* const s_bundlerPluginRunOnResolvePluginsCode = "(function (_,g,y,F,j,q){\"use strict\";const w=[\"entry-point\",\"import-statement\",\"require-call\",\"dynamic-import\",\"require-resolve\",\"import-rule\",\"url-token\",\"internal\"][j];var z=(async(A,B,C,E,G)=>{var{onResolve:H,onLoad:J}=this,K=H.@get(B);if(!K)return this.onResolveAsync(F,null,null,null),null;for(let[T,U]of K)if(T.test(A)){var M=U({path:A,importer:C,namespace:B,resolveDir:G,kind:E});while(M&&@isPromise(M)&&(@getPromiseInternalField(M,@promiseFieldFlags)&@promiseStateMask)===@promiseStateFulfilled)M=@getPromiseInternalField(M,@promiseFieldReactionsOrResult);if(M&&@isPromise(M))M=await M;if(!M||!@isObject(M))continue;var{path:O,namespace:Q=B,external:S}=M;if(typeof O!==\"string\")@throwTypeError(\"onResolve: expected 'path' to be a string\");if(typeof Q!==\"string\")@throwTypeError(\"onResolve: expected 'namespace' to be a string\");if(!O)continue;if(!Q)Q=B;if(typeof S!==\"boolean\"&&!@isUndefinedOrNull(S))@throwTypeError(\"onResolve: expected 'external' to be boolean\");if(!S){if(Q===\"file\"){if(darwin!==\"win32\"){if(O[0]!==\"/\"||O.includes(\"..\"))@throwTypeError('onResolve plugin \"path\" must be absolute when the namespace is \"file\"')}}if(Q===\"dataurl\"){if(!O.startsWith(\"data:\"))@throwTypeError('onResolve plugin \"path\" must start with \"data:\" when the namespace is \"dataurl\"')}if(Q&&Q!==\"file\"&&(!J||!J.@has(Q)))@throwTypeError(`Expected onLoad plugin for namespace ${Q} to exist`)}return this.onResolveAsync(F,O,Q,S),null}return this.onResolveAsync(F,null,null,null),null})(_,g,y,w,q);while(z&&@isPromise(z)&&(@getPromiseInternalField(z,@promiseFieldFlags)&@promiseStateMask)===@promiseStateFulfilled)z=@getPromiseInternalField(z,@promiseFieldReactionsOrResult);if(z&&@isPromise(z))z.then(()=>{},(A)=>{this.addError(F,A,0)})})\n";
// runOnLoadPlugins
const JSC::ConstructAbility s_bundlerPluginRunOnLoadPluginsCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
@@ -32,7 +32,7 @@ const JSC::ConstructorKind s_bundlerPluginRunOnLoadPluginsCodeConstructorKind =
const JSC::ImplementationVisibility s_bundlerPluginRunOnLoadPluginsCodeImplementationVisibility = JSC::ImplementationVisibility::Public;
const int s_bundlerPluginRunOnLoadPluginsCodeLength = 1325;
static const JSC::Intrinsic s_bundlerPluginRunOnLoadPluginsCodeIntrinsic = JSC::NoIntrinsic;
-const char* const s_bundlerPluginRunOnLoadPluginsCode = "(function (b,g,j,q){\"use strict\";const v={jsx:0,js:1,ts:2,tsx:3,css:4,file:5,json:6,toml:7,wasm:8,napi:9,base64:10,dataurl:11,text:12},w=[\"jsx\",\"js\",\"ts\",\"tsx\",\"css\",\"file\",\"json\",\"toml\",\"wasm\",\"napi\",\"base64\",\"dataurl\",\"text\"][q];var x=(async(y,z,B,C)=>{var F=this.onLoad.@get(B);if(!F)return this.onLoadAsync(y,null,null),null;for(let[K,Q]of F)if(K.test(z)){var G=Q({path:z,namespace:B,loader:C});while(G&&@isPromise(G)&&(@getPromiseInternalField(G,@promiseFieldFlags)&@promiseStateMask)===@promiseStateFulfilled)G=@getPromiseInternalField(G,@promiseFieldReactionsOrResult);if(G&&@isPromise(G))G=await G;if(!G||!@isObject(G))continue;var{contents:H,loader:J=C}=G;if(typeof H!==\"string\"&&!@isTypedArrayView(H))@throwTypeError('onLoad plugins must return an object with \"contents\" as a string or Uint8Array');if(typeof J!==\"string\")@throwTypeError('onLoad plugins must return an object with \"loader\" as a string');const T=v[J];if(T===@undefined)@throwTypeError(`Loader ${J} is not supported.`);return this.onLoadAsync(y,H,T),null}return this.onLoadAsync(y,null,null),null})(b,g,j,w);while(x&&@isPromise(x)&&(@getPromiseInternalField(x,@promiseFieldFlags)&@promiseStateMask)===@promiseStateFulfilled)x=@getPromiseInternalField(x,@promiseFieldReactionsOrResult);if(x&&@isPromise(x))x.then(()=>{},(y)=>{this.addError(b,y,1)})})\n";
+const char* const s_bundlerPluginRunOnLoadPluginsCode = "(function (w,y,_,g){\"use strict\";const j={jsx:0,js:1,ts:2,tsx:3,css:4,file:5,json:6,toml:7,wasm:8,napi:9,base64:10,dataurl:11,text:12},q=[\"jsx\",\"js\",\"ts\",\"tsx\",\"css\",\"file\",\"json\",\"toml\",\"wasm\",\"napi\",\"base64\",\"dataurl\",\"text\"][g];var v=(async(x,z,B,C)=>{var F=this.onLoad.@get(B);if(!F)return this.onLoadAsync(x,null,null),null;for(let[K,P]of F)if(K.test(z)){var G=P({path:z,namespace:B,loader:C});while(G&&@isPromise(G)&&(@getPromiseInternalField(G,@promiseFieldFlags)&@promiseStateMask)===@promiseStateFulfilled)G=@getPromiseInternalField(G,@promiseFieldReactionsOrResult);if(G&&@isPromise(G))G=await G;if(!G||!@isObject(G))continue;var{contents:H,loader:J=C}=G;if(typeof H!==\"string\"&&!@isTypedArrayView(H))@throwTypeError('onLoad plugins must return an object with \"contents\" as a string or Uint8Array');if(typeof J!==\"string\")@throwTypeError('onLoad plugins must return an object with \"loader\" as a string');const Q=j[J];if(Q===@undefined)@throwTypeError(`Loader ${J} is not supported.`);return this.onLoadAsync(x,H,Q),null}return this.onLoadAsync(x,null,null),null})(w,y,_,q);while(v&&@isPromise(v)&&(@getPromiseInternalField(v,@promiseFieldFlags)&@promiseStateMask)===@promiseStateFulfilled)v=@getPromiseInternalField(v,@promiseFieldReactionsOrResult);if(v&&@isPromise(v))v.then(()=>{},(x)=>{this.addError(w,x,1)})})\n";
#define DEFINE_BUILTIN_GENERATOR(codeName, functionName, overriddenName, argumentCount) \
JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \
diff --git a/src/js/out/WebCoreJSBuiltins.h b/src/js/out/WebCoreJSBuiltins.h
index 3511c6190..d40c09922 100644
--- a/src/js/out/WebCoreJSBuiltins.h
+++ b/src/js/out/WebCoreJSBuiltins.h
@@ -42,7 +42,7 @@ extern const JSC::ImplementationVisibility s_bundlerPluginRunOnLoadPluginsCodeIm
#define WEBCORE_FOREACH_BUNDLERPLUGIN_BUILTIN_DATA(macro) \
macro(runSetupFunction, bundlerPluginRunSetupFunction, 2) \
- macro(runOnResolvePlugins, bundlerPluginRunOnResolvePlugins, 5) \
+ macro(runOnResolvePlugins, bundlerPluginRunOnResolvePlugins, 7) \
macro(runOnLoadPlugins, bundlerPluginRunOnLoadPlugins, 4) \
#define WEBCORE_FOREACH_BUNDLERPLUGIN_BUILTIN_CODE(macro) \
diff --git a/test/bun.lockb b/test/bun.lockb
index 3b60656d2..b23302fe5 100755
--- a/test/bun.lockb
+++ b/test/bun.lockb
Binary files differ
diff --git a/test/bundler/bundler_plugin.test.ts b/test/bundler/bundler_plugin.test.ts
index f8e5d8d59..d2afc3d3f 100644
--- a/test/bundler/bundler_plugin.test.ts
+++ b/test/bundler/bundler_plugin.test.ts
@@ -830,4 +830,32 @@ describe("bundler", () => {
},
};
});
+ itBundled("plugin/resolveDir", ({ getConfigRef, root }) => {
+ return {
+ run: true,
+ files: {
+ "index.ts": /* ts */ `
+ import "./foo.magic";
+ `,
+ },
+ entryPoints: ["./index.ts"],
+ plugins(build) {
+ build.onResolve({ "filter": /.magic$/ }, args => {
+ console.log({ root, resolveDir: args.resolveDir });
+ expect(args.resolveDir).toBeDefined();
+ expect(args.resolveDir!.replace("/private", "")).toEqual(root);
+ return {
+ path: "magic",
+ "namespace": "magic",
+ };
+ });
+ build.onLoad({ namespace: "magic", "filter": /.*/ }, _args => {
+ return {
+ contents: "",
+ loader: "ts",
+ };
+ });
+ },
+ };
+ });
});
diff --git a/test/bundler/expectBundled.ts b/test/bundler/expectBundled.ts
index 3ec68ac4e..a5cc344c3 100644
--- a/test/bundler/expectBundled.ts
+++ b/test/bundler/expectBundled.ts
@@ -1186,6 +1186,9 @@ for (const [key, blob] of build.outputs) {
},
stdio: ["ignore", "pipe", "pipe"],
});
+ if (DEBUG) {
+ console.log({ stdout: stdout!.toString("utf-8"), stderr: stderr!.toString("utf-8") });
+ }
if (run.error) {
if (success) {
@@ -1287,6 +1290,7 @@ export function itBundled(
): BundlerTestRef {
if (typeof opts === "function") {
const fn = opts;
+ // console.log("outbase", outBase);
opts = opts({ root: path.join(outBase, id.replaceAll("/", path.sep)), getConfigRef });
// @ts-expect-error
opts._referenceFn = fn;