const fs = bun.fs; const bun = @import("bun"); const logger = bun.logger; const std = @import("std"); const Index = @import("ast/base.zig").Index; const Api = @import("./api/schema.zig").Api; pub const ImportKind = enum(u8) { /// An entry point provided to `bun run` or `bun` entry_point_run = 0, /// An entry point provided to `bun build` or `Bun.build` entry_point_build = 1, /// An ES6 import or re-export statement stmt = 2, /// A call to "require()" require = 3, /// An "import()" expression with a string argument dynamic = 4, /// A call to "require.resolve()" require_resolve = 5, /// A CSS "@import" rule at = 6, /// A CSS "@import" rule with import conditions at_conditional = 7, /// A CSS "url(...)" token url = 8, /// A CSS "composes" property composes = 9, html_manifest = 10, internal = 11, pub const Label = std.EnumArray(ImportKind, []const u8); pub const all_labels: Label = brk: { // If these are changed, make sure to update // - src/js/builtins/codegen/replacements.ts // - packages/bun-types/bun.d.ts var labels = Label.initFill(""); labels.set(ImportKind.entry_point_run, "entry-point-run"); labels.set(ImportKind.entry_point_build, "entry-point-build"); labels.set(ImportKind.stmt, "import-statement"); labels.set(ImportKind.require, "require-call"); labels.set(ImportKind.dynamic, "dynamic-import"); labels.set(ImportKind.require_resolve, "require-resolve"); labels.set(ImportKind.at, "import-rule"); labels.set(ImportKind.url, "url-token"); labels.set(ImportKind.composes, "composes"); labels.set(ImportKind.internal, "internal"); labels.set(ImportKind.html_manifest, "html_manifest"); break :brk labels; }; pub const error_labels: Label = brk: { var labels = Label.initFill(""); labels.set(ImportKind.entry_point_run, "entry point (run)"); labels.set(ImportKind.entry_point_build, "entry point (build)"); labels.set(ImportKind.stmt, "import"); labels.set(ImportKind.require, "require()"); labels.set(ImportKind.dynamic, "import()"); labels.set(ImportKind.require_resolve, "require.resolve()"); labels.set(ImportKind.at, "@import"); labels.set(ImportKind.url, "url()"); labels.set(ImportKind.internal, ""); labels.set(ImportKind.composes, "composes"); labels.set(ImportKind.html_manifest, "HTML import"); break :brk labels; }; pub inline fn label(this: ImportKind) []const u8 { return all_labels.get(this); } pub inline fn errorLabel(this: ImportKind) []const u8 { return error_labels.get(this); } pub inline fn isCommonJS(this: ImportKind) bool { return switch (this) { .require, .require_resolve => true, else => false, }; } pub fn jsonStringify(self: @This(), writer: anytype) !void { return try writer.write(@tagName(self)); } pub fn isFromCSS(k: ImportKind) bool { return k == .at_conditional or k == .at or k == .url or k == .composes; } pub fn toAPI(k: ImportKind) Api.ImportKind { return switch (k) { ImportKind.entry_point => Api.ImportKind.entry_point, ImportKind.stmt => Api.ImportKind.stmt, ImportKind.require => Api.ImportKind.require, ImportKind.dynamic => Api.ImportKind.dynamic, ImportKind.require_resolve => Api.ImportKind.require_resolve, ImportKind.at => Api.ImportKind.at, ImportKind.url => Api.ImportKind.url, else => Api.ImportKind.internal, }; } }; pub const ImportRecord = struct { pub const Index = bun.GenericIndex(u32, ImportRecord); range: logger.Range, path: fs.Path, kind: ImportKind, tag: Tag = .none, loader: ?bun.options.Loader = null, source_index: bun.JSAst.Index = .invalid, /// True for the following cases: /// /// try { require('x') } catch { handle } /// try { await import('x') } catch { handle } /// try { require.resolve('x') } catch { handle } /// import('x').catch(handle) /// import('x').then(_, handle) /// /// In these cases we shouldn't generate an error if the path could not be /// resolved. handles_import_errors: bool = false, is_internal: bool = false, /// Sometimes the parser creates an import record and decides it isn't needed. /// For example, TypeScript code may have import statements that later turn /// out to be type-only imports after analyzing the whole file. is_unused: bool = false, /// True if this import uses the "defer" keyword is_deferred: bool = false, /// If this is true, the import contains syntax like "* as ns". This is used /// to determine whether modules that have no exports need to be wrapped in a /// CommonJS wrapper or not. contains_import_star: bool = false, /// If this is true, the import contains an import for the alias "default", /// either via the "import x from" or "import {default as x} from" syntax. contains_default_alias: bool = false, contains_es_module_alias: bool = false, /// If true, this "export * from 'path'" statement is evaluated at run-time by /// calling the "__reExport()" helper function calls_runtime_re_export_fn: bool = false, /// True for require calls like this: "try { require() } catch {}". In this /// case we shouldn't generate an error if the path could not be resolved. is_inside_try_body: bool = false, /// If true, this was originally written as a bare "import 'file'" statement was_originally_bare_import: bool = false, was_originally_require: bool = false, /// If a macro used , it will be tracked here. was_injected_by_macro: bool = false, /// If true, this import can be removed if it's unused is_external_without_side_effects: bool = false, /// Tell the printer to print the record as "foo:my-path" instead of "path" /// where "foo" is the namespace /// /// Used to prevent running resolve plugins multiple times for the same path print_namespace_in_path: bool = false, wrap_with_to_esm: bool = false, wrap_with_to_commonjs: bool = false, pub const List = bun.BabyList(ImportRecord); pub const Tag = enum { /// A normal import to a user's source file none, /// An import to 'bun' bun, /// An import to 'bun:test' bun_test, /// A builtin module, such as `node:fs` or `bun:sqlite` builtin, /// An import to the internal runtime runtime, /// A 'macro:' import namespace or 'with { type: "macro" }' macro, /// For Bun Kit, if a module in the server graph should actually /// crossover to the SSR graph. See bake.Framework.ServerComponents.separate_ssr_graph bake_resolve_to_ssr_graph, tailwind, pub inline fn isRuntime(this: Tag) bool { return this == .runtime; } pub inline fn isInternal(this: Tag) bool { return @intFromEnum(this) >= @intFromEnum(Tag.runtime); } }; pub const PrintMode = enum { normal, import_path, css, napi_module, }; };