aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js/builtins/ts/BundlerPlugin.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/bun.js/builtins/ts/BundlerPlugin.ts')
-rw-r--r--src/bun.js/builtins/ts/BundlerPlugin.ts370
1 files changed, 0 insertions, 370 deletions
diff --git a/src/bun.js/builtins/ts/BundlerPlugin.ts b/src/bun.js/builtins/ts/BundlerPlugin.ts
deleted file mode 100644
index 831a6614e..000000000
--- a/src/bun.js/builtins/ts/BundlerPlugin.ts
+++ /dev/null
@@ -1,370 +0,0 @@
-import type {
- AnyFunction,
- BuildConfig,
- BunPlugin,
- OnLoadCallback,
- OnLoadResult,
- OnLoadResultObject,
- OnLoadResultSourceCode,
- OnResolveCallback,
- PluginBuilder,
- PluginConstraints,
-} from "bun";
-
-// This API expects 4 functions:
-// It should be generic enough to reuse for Bun.plugin() eventually, too.
-interface BundlerPlugin {
- onLoad: Map<string, [RegExp, OnLoadCallback][]>;
- onResolve: Map<string, [RegExp, OnResolveCallback][]>;
- onLoadAsync(
- internalID,
- sourceCode: string | Uint8Array | ArrayBuffer | DataView | null,
- loaderKey: number | null,
- ): void;
- onResolveAsync(internalID, a, b, c): void;
- addError(internalID, error, number): void;
- addFilter(filter, namespace, number): void;
-}
-
-// Extra types
-type Setup = BunPlugin["setup"];
-type MinifyObj = Exclude<BuildConfig["minify"], boolean>;
-interface BuildConfigExt extends BuildConfig {
- // we support esbuild-style entryPoints
- entryPoints?: string[];
- // plugins is guaranteed to not be null
- plugins: BunPlugin[];
-}
-interface PluginBuilderExt extends PluginBuilder {
- // these functions aren't implemented yet, so we dont publicly expose them
- resolve: AnyFunction;
- onStart: AnyFunction;
- onEnd: AnyFunction;
- onDispose: AnyFunction;
- // we partially support initialOptions. it's read-only and a subset of
- // all options mapped to their esbuild names
- initialOptions: any;
- // we set this to an empty object
- esbuild: any;
-}
-
-export function runSetupFunction(this: BundlerPlugin, setup: Setup, config: BuildConfigExt) {
- var onLoadPlugins = new Map<string, [RegExp, AnyFunction][]>();
- var onResolvePlugins = new Map<string, [RegExp, AnyFunction][]>();
-
- function validate(filterObject: PluginConstraints, callback, map) {
- if (!filterObject || !$isObject(filterObject)) {
- throw new TypeError('Expected an object with "filter" RegExp');
- }
-
- if (!callback || !$isCallable(callback)) {
- throw new TypeError("callback must be a function");
- }
-
- var { filter, namespace = "file" } = filterObject;
-
- if (!filter) {
- throw new TypeError('Expected an object with "filter" RegExp');
- }
-
- if (!$isRegExpObject(filter)) {
- throw new TypeError("filter must be a RegExp");
- }
-
- if (namespace && !(typeof namespace === "string")) {
- throw new TypeError("namespace must be a string");
- }
-
- if ((namespace?.length ?? 0) === 0) {
- namespace = "file";
- }
-
- if (!/^([/$a-zA-Z0-9_\\-]+)$/.test(namespace)) {
- throw new TypeError("namespace can only contain $a-zA-Z0-9_\\-");
- }
-
- var callbacks = map.$get(namespace);
-
- if (!callbacks) {
- map.$set(namespace, [[filter, callback]]);
- } else {
- $arrayPush(callbacks, [filter, callback]);
- }
- }
-
- function onLoad(filterObject, callback) {
- validate(filterObject, callback, onLoadPlugins);
- }
-
- function onResolve(filterObject, callback) {
- validate(filterObject, callback, onResolvePlugins);
- }
-
- const processSetupResult = () => {
- var anyOnLoad = false,
- anyOnResolve = false;
-
- for (var [namespace, callbacks] of onLoadPlugins.entries()) {
- for (var [filter] of callbacks) {
- this.addFilter(filter, namespace, 1);
- anyOnLoad = true;
- }
- }
-
- for (var [namespace, callbacks] of onResolvePlugins.entries()) {
- for (var [filter] of callbacks) {
- this.addFilter(filter, namespace, 0);
- anyOnResolve = true;
- }
- }
-
- if (anyOnResolve) {
- var onResolveObject = this.onResolve;
- if (!onResolveObject) {
- this.onResolve = onResolvePlugins;
- } else {
- for (var [namespace, callbacks] of onResolvePlugins.entries()) {
- var existing = onResolveObject.$get(namespace) as [RegExp, AnyFunction][];
-
- if (!existing) {
- onResolveObject.$set(namespace, callbacks);
- } else {
- onResolveObject.$set(namespace, existing.concat(callbacks));
- }
- }
- }
- }
-
- if (anyOnLoad) {
- var onLoadObject = this.onLoad;
- if (!onLoadObject) {
- this.onLoad = onLoadPlugins;
- } else {
- for (var [namespace, callbacks] of onLoadPlugins.entries()) {
- var existing = onLoadObject.$get(namespace) as [RegExp, AnyFunction][];
-
- if (!existing) {
- onLoadObject.$set(namespace, callbacks);
- } else {
- onLoadObject.$set(namespace, existing.concat(callbacks));
- }
- }
- }
- }
-
- return anyOnLoad || anyOnResolve;
- };
-
- var setupResult = setup({
- config: config,
- onDispose: notImplementedIssueFn(2771, "On-dispose callbacks"),
- onEnd: notImplementedIssueFn(2771, "On-end callbacks"),
- onLoad,
- onResolve,
- onStart: notImplementedIssueFn(2771, "On-start callbacks"),
- resolve: notImplementedIssueFn(2771, "build.resolve()"),
- // esbuild's options argument is different, we provide some interop
- initialOptions: {
- ...config,
- bundle: true,
- entryPoints: config.entrypoints ?? config.entryPoints ?? [],
- minify: typeof config.minify === "boolean" ? config.minify : false,
- minifyIdentifiers: config.minify === true || (config.minify as MinifyObj)?.identifiers,
- minifyWhitespace: config.minify === true || (config.minify as MinifyObj)?.whitespace,
- minifySyntax: config.minify === true || (config.minify as MinifyObj)?.syntax,
- outbase: config.root,
- platform: config.target === "bun" ? "node" : config.target,
- },
- esbuild: {},
- } satisfies PluginBuilderExt as PluginBuilder);
-
- if (setupResult && $isPromise(setupResult)) {
- if ($getPromiseInternalField(setupResult, $promiseFieldFlags) & $promiseStateFulfilled) {
- setupResult = $getPromiseInternalField(setupResult, $promiseFieldReactionsOrResult);
- } else {
- return setupResult.$then(processSetupResult);
- }
- }
-
- return processSetupResult();
-}
-
-export function runOnResolvePlugins(this: BundlerPlugin, specifier, inputNamespace, importer, internalID, kindId) {
- // Must be kept in sync with ImportRecord.label
- const kind = $ImportKindIdToLabel[kindId];
-
- var promiseResult: any = (async (inputPath, inputNamespace, importer, kind) => {
- var { onResolve, onLoad } = this;
- var results = onResolve.$get(inputNamespace);
- if (!results) {
- this.onResolveAsync(internalID, null, null, null);
- return null;
- }
-
- for (let [filter, callback] of results) {
- if (filter.test(inputPath)) {
- var result = callback({
- path: inputPath,
- importer,
- namespace: inputNamespace,
- // resolveDir
- kind,
- // pluginData
- });
-
- while (
- result &&
- $isPromise(result) &&
- ($getPromiseInternalField(result, $promiseFieldFlags) & $promiseStateMask) === $promiseStateFulfilled
- ) {
- result = $getPromiseInternalField(result, $promiseFieldReactionsOrResult);
- }
-
- if (result && $isPromise(result)) {
- result = await result;
- }
-
- if (!result || !$isObject(result)) {
- continue;
- }
-
- 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 (!path) {
- continue;
- }
-
- if (!userNamespace) {
- userNamespace = inputNamespace;
- }
- if (typeof external !== "boolean" && !$isUndefinedOrNull(external)) {
- throw new TypeError('onResolve plugins "external" field must be boolean or unspecified');
- }
-
- if (!external) {
- if (userNamespace === "file") {
- if (process.platform !== "win32") {
- if (path[0] !== "/" || path.includes("..")) {
- throw new TypeError('onResolve plugin "path" must be absolute when the namespace is "file"');
- }
- } else {
- // TODO: Windows
- }
- }
- if (userNamespace === "dataurl") {
- if (!path.startsWith("data:")) {
- throw new TypeError('onResolve plugin "path" must start with "data:" when the namespace is "dataurl"');
- }
- }
-
- if (userNamespace && userNamespace !== "file" && (!onLoad || !onLoad.$has(userNamespace))) {
- throw new TypeError(`Expected onLoad plugin for namespace ${userNamespace} to exist`);
- }
- }
- this.onResolveAsync(internalID, path, userNamespace, external);
- return null;
- }
- }
-
- this.onResolveAsync(internalID, null, null, null);
- return null;
- })(specifier, inputNamespace, importer, kind);
-
- while (
- promiseResult &&
- $isPromise(promiseResult) &&
- ($getPromiseInternalField(promiseResult, $promiseFieldFlags) & $promiseStateMask) === $promiseStateFulfilled
- ) {
- promiseResult = $getPromiseInternalField(promiseResult, $promiseFieldReactionsOrResult);
- }
-
- if (promiseResult && $isPromise(promiseResult)) {
- promiseResult.then(
- () => {},
- e => {
- this.addError(internalID, e, 0);
- },
- );
- }
-}
-
-export function runOnLoadPlugins(this: BundlerPlugin, internalID, path, namespace, defaultLoaderId) {
- const LOADERS_MAP = $LoaderLabelToId;
- const loaderName = $LoaderIdToLabel[defaultLoaderId];
-
- var promiseResult = (async (internalID, path, namespace, defaultLoader) => {
- var results = this.onLoad.$get(namespace);
- if (!results) {
- this.onLoadAsync(internalID, null, null);
- return null;
- }
-
- for (let [filter, callback] of results) {
- if (filter.test(path)) {
- var result = callback({
- path,
- namespace,
- // suffix
- // pluginData
- loader: defaultLoader,
- });
-
- while (
- result &&
- $isPromise(result) &&
- ($getPromiseInternalField(result, $promiseFieldFlags) & $promiseStateMask) === $promiseStateFulfilled
- ) {
- result = $getPromiseInternalField(result, $promiseFieldReactionsOrResult);
- }
-
- if (result && $isPromise(result)) {
- result = await result;
- }
-
- if (!result || !$isObject(result)) {
- continue;
- }
-
- var { contents, loader = defaultLoader } = result as OnLoadResultSourceCode & OnLoadResultObject;
- if (!(typeof contents === "string") && !$isTypedArrayView(contents)) {
- throw new TypeError('onLoad plugins must return an object with "contents" as a string or Uint8Array');
- }
-
- if (!(typeof loader === "string")) {
- throw new TypeError('onLoad plugins must return an object with "loader" as a string');
- }
-
- const chosenLoader = LOADERS_MAP[loader];
- if (chosenLoader === undefined) {
- throw new TypeError(`Loader ${loader} is not supported.`);
- }
-
- this.onLoadAsync(internalID, contents, chosenLoader);
- return null;
- }
- }
-
- this.onLoadAsync(internalID, null, null);
- return null;
- })(internalID, path, namespace, loaderName);
-
- while (
- promiseResult &&
- $isPromise(promiseResult) &&
- ($getPromiseInternalField(promiseResult, $promiseFieldFlags) & $promiseStateMask) === $promiseStateFulfilled
- ) {
- promiseResult = $getPromiseInternalField(promiseResult, $promiseFieldReactionsOrResult);
- }
-
- if (promiseResult && $isPromise(promiseResult)) {
- promiseResult.then(
- () => {},
- e => {
- this.addError(internalID, e, 1);
- },
- );
- }
-}