From 07dc1ae547d226f0e122922d3452e00ac6a8a221 Mon Sep 17 00:00:00 2001 From: Zack Radisic <56137411+zackradisic@users.noreply.github.com> Date: Fri, 8 Nov 2024 16:38:30 -0800 Subject: [PATCH] `.defer()`, `.onStart()`, and some small CSS changes (#15041) --- docs/runtime/plugins.md | 251 ++- packages/bun-types/bun.d.ts | 24 +- src/bun.js/api/JSBundler.zig | 94 +- src/bun.js/bindings/JSBundlerPlugin.cpp | 52 +- src/bun.js/bindings/JSBundlerPlugin.h | 3 +- src/bun.js/event_loop.zig | 5 + src/bundler/bundle_v2.zig | 154 +- src/codegen/replacements.ts | 2 +- src/css/css_parser.zig | 715 +++++--- src/css/media_query.zig | 2 +- src/css/properties/generate_properties.ts | 20 +- src/css/properties/properties_generated.zig | 1618 +++++++++++-------- src/css/rules/rules.zig | 2 + src/css/rules/tailwind.zig | 60 + src/import_record.zig | 1 + src/js/builtins/BundlerPlugin.ts | 53 +- test/bundler/expectBundled.ts | 5 +- test/js/bun/plugin/plugins.test.ts | 635 +++++++- 18 files changed, 2682 insertions(+), 1014 deletions(-) create mode 100644 src/css/rules/tailwind.zig diff --git a/docs/runtime/plugins.md b/docs/runtime/plugins.md index 5fd38c3694dcdf..4ff38e1992c32b 100644 --- a/docs/runtime/plugins.md +++ b/docs/runtime/plugins.md @@ -302,7 +302,7 @@ require("my-object-virtual-module"); // { baz: "quix" } await import("my-object-virtual-module"); // { baz: "quix" } ``` -## Reading the config +## Reading or modifying the config Plugins can read and write to the [build config](https://bun.sh/docs/bundler#api) with `build.config`. @@ -327,7 +327,43 @@ Bun.build({ }); ``` -## Reference +{% callout %} + +**NOTE**: Plugin lifcycle callbacks (`onStart()`, `onResolve()`, etc.) do not have the ability to modify the `build.config` object in the `setup()` function. If you want to mutate `build.config`, you must do so directly in the `setup()` function: + +```ts +Bun.build({ + entrypoints: ["./app.ts"], + outdir: "./dist", + sourcemap: "external", + plugins: [ + { + name: "demo", + setup(build) { + // ✅ good! modifying it directly in the setup() function + build.config.minify = true; + + build.onStart(() => { + // 🚫 uh-oh! this won't work! + build.config.minify = false; + }); + }, + }, + ], +}); +``` + +{% /callout %} + +## Lifecycle callbacks + +Plugins can register callbacks to be run at various points in the lifecycle of a bundle: + +- [`onStart()`](#onstart): Run once the bundler has started a bundle +- [`onResolve()`](#onresolve): Run before a module is resolved +- [`onLoad()`](#onload): Run before a module is loaded. + +A rough overview of the types (please refer to Bun's `bun.d.ts` for the full type definitions): ```ts namespace Bun { @@ -338,6 +374,7 @@ namespace Bun { } type PluginBuilder = { + onStart(callback: () => void): void; onResolve: ( args: { filter: RegExp; namespace?: string }, callback: (args: { path: string; importer: string }) => { @@ -356,7 +393,213 @@ type PluginBuilder = { config: BuildConfig; }; -type Loader = "js" | "jsx" | "ts" | "tsx" | "json" | "toml" | "object"; +type Loader = "js" | "jsx" | "ts" | "tsx" | "css" | "json" | "toml" | "object"; +``` + +### Namespaces + +`onLoad` and `onResolve` accept an optional `namespace` string. What is a namespaace? + +Every module has a namespace. Namespaces are used to prefix the import in transpiled code; for instance, a loader with a `filter: /\.yaml$/` and `namespace: "yaml:"` will transform an import from `./myfile.yaml` into `yaml:./myfile.yaml`. + +The default namespace is `"file"` and it is not necessary to specify it, for instance: `import myModule frmo "./my-module.ts"` is the same as `import myModule from "file:./my-module.ts"`. + +Other common namespaces are: + +- `"bun"`: for Bun-specific modules (e.g. `"bun:test"`, `"bun:sqlite"`) +- `"node"`: for Node.js modules (e.g. `"node:fs"`, `"node:path"`) + +### `onStart` + +```ts +onStart(callback: () => void): Promise | void; +``` + +Registers a callback to be run when the bundler starts a new bundle. + +```ts +import { plugin } from "bun"; + +plugin({ + name: "onStart example", + + setup(build) { + build.onStart(() => { + console.log("Bundle started!"); + }); + }, +}); +``` + +The callback can return a `Promise`. After the bundle process has initialized, the bundler waits until all `onStart()` callbacks have completed before continuing. + +For example: + +```ts +const result = await Bun.build({ + entrypoints: ["./app.ts"], + outdir: "./dist", + sourcemap: "external", + plugins: [ + { + name: "Sleep for 10 seconds", + setup(build) { + build.onStart(async () => { + await Bunlog.sleep(10_000); + }); + }, + }, + { + name: "Log bundle time to a file", + setup(build) { + build.onStart(async () => { + const now = Date.now(); + await Bun.$`echo ${now} > bundle-time.txt`; + }); + }, + }, + ], +}); +``` + +In the above example, Bun will wait until the first `onStart()` (sleeping for 10 seconds) has completed, _as well as_ the second `onStart()` (writing the bundle time to a file). + +Note that `onStart()` callbacks (like every other lifecycle callback) do not have the ability to modify the `build.config` object. If you want to mutate `build.config`, you must do so directly in the `setup()` function. + +### `onResolve` + +```ts +onResolve( + args: { filter: RegExp; namespace?: string }, + callback: (args: { path: string; importer: string }) => { + path: string; + namespace?: string; + } | void, +): void; +``` + +To bundle your project, Bun walks down the dependency tree of all modules in your project. For each imported module, Bun actually has to find and read that module. The "finding" part is known as "resolving" a module. + +The `onResolve()` plugin lifecycle callback allows you to configure how a module is resolved. + +The first argument to `onResolve()` is an object with a `filter` and [`namespace`](#what-is-a-namespace) property. The filter is a regular expression which is run on the import string. Effectively, these allow you to filter which modules your custom resolution logic will apply to. + +The second argument to `onResolve()` is a callback which is run for each module import Bun finds that matches the `filter` and `namespace` defined in the first argument. + +The callback receives as input the _path_ to the matching module. The callback can return a _new path_ for the module. Bun will read the contents of the _new path_ and parse it as a module. + +For example, redirecting all imports to `images/` to `./public/images/`: + +```ts +import { plugin } from "bun"; + +plugin({ + name: "onResolve example", + setup(build) { + build.onResolve({ filter: /.*/, namespace: "file" }, args => { + if (args.path.startsWith("images/")) { + return { + path: args.path.replace("images/", "./public/images/"), + }; + } + }); + }, +}); +``` + +### `onLoad` + +```ts +onLoad( + args: { filter: RegExp; namespace?: string }, + callback: (args: { path: string, importer: string, namespace: string, kind: ImportKind }) => { + loader?: Loader; + contents?: string; + exports?: Record; + }, +): void; +``` + +After Bun's bundler has resolved a module, it needs to read the contents of the module and parse it. + +The `onLoad()` plugin lifecycle callback allows you to modify the _contents_ of a module before it is read and parsed by Bun. + +Like `onResolve()`, the first argument to `onLoad()` allows you to filter which modules this invocation of `onLoad()` will apply to. + +The second argument to `onLoad()` is a callback which is run for each matching module _before_ Bun loads the contents of the module into memory. + +This callback receives as input the _path_ to the matching module, the _importer_ of the module (the module that imported the module), the _namespace_ of the module, and the _kind_ of the module. + +The callback can return a new `contents` string for the module as well as a new `loader`. + +For example: + +```ts +import { plugin } from "bun"; + +plugin({ + name: "env plugin", + setup(build) { + build.onLoad({ filter: /env/, namespace: "file" }, args => { + return { + contents: `export default ${JSON.stringify(process.env)}`, + loader: "js", + }; + }); + }, +}); +``` + +This plugin will transform all imports of the form `import env from "env"` into a JavaScript module that exports the current environment variables. + +#### `.defer()` + +One of the arguments passed to the `onLoad` callback is a `defer` function. This function returns a `Promise` that is resolved when all _other_ modules have been loaded. + +This allows you to delay execution of the `onLoad` callback until all other modules have been loaded. + +This is useful for returning contens of a module that depends on other modules. + +##### Example: tracking and reporting unused exports + +```ts +import { plugin } from "bun"; + +plugin({ + name: "track imports", + setup(build) { + const transpiler = new Bun.Transpiler(); + + let trackedImports: Record = {}; + + // Each module that goes through this onLoad callback + // will record its imports in `trackedImports` + build.onLoad({ filter: /\.ts/ }, async ({ path }) => { + const contents = await Bun.file(path).arrayBuffer(); + + const imports = transpiler.scanImports(contents); + + for (const i of imports) { + trackedImports[i.path] = (trackedImports[i.path] || 0) + 1; + } + + return undefined; + }); + + build.onLoad({ filter: /stats\.json/ }, async ({ defer }) => { + // Wait for all files to be loaded, ensuring + // that every file goes through the above `onLoad()` function + // and their imports tracked + await defer(); + + // Emit JSON containing the stats of each import + return { + contents: `export default ${JSON.stringify(trackedImports)}`, + loader: "json", + }; + }); + }, +}); ``` -The `onLoad` method optionally accepts a `namespace` in addition to the `filter` regex. This namespace will be be used to prefix the import in transpiled code; for instance, a loader with a `filter: /\.yaml$/` and `namespace: "yaml:"` will transform an import from `./myfile.yaml` into `yaml:./myfile.yaml`. +Note that the `.defer()` function currently has the limitation that it can only be called once per `onLoad` callback. diff --git a/packages/bun-types/bun.d.ts b/packages/bun-types/bun.d.ts index 6e2085e3b88cd1..e88ca60caf640c 100644 --- a/packages/bun-types/bun.d.ts +++ b/packages/bun-types/bun.d.ts @@ -3785,7 +3785,7 @@ declare module "bun" { | "browser"; /** https://bun.sh/docs/bundler/loaders */ - type Loader = "js" | "jsx" | "ts" | "tsx" | "json" | "toml" | "file" | "napi" | "wasm" | "text"; + type Loader = "js" | "jsx" | "ts" | "tsx" | "json" | "toml" | "file" | "napi" | "wasm" | "text" | "css"; interface PluginConstraints { /** @@ -3873,10 +3873,18 @@ declare module "bun" { * The default loader for this file extension */ loader: Loader; + + /** + * Defer the execution of this callback until all other modules have been parsed. + * + * @returns Promise which will be resolved when all modules have been parsed + */ + defer: () => Promise; } type OnLoadResult = OnLoadResultSourceCode | OnLoadResultObject | undefined; type OnLoadCallback = (args: OnLoadArgs) => OnLoadResult | Promise; + type OnStartCallback = () => void | Promise; interface OnResolveArgs { /** @@ -3953,6 +3961,20 @@ declare module "bun" { * ``` */ onResolve(constraints: PluginConstraints, callback: OnResolveCallback): void; + /** + * Register a callback which will be invoked when bundling starts. + * @example + * ```ts + * Bun.plugin({ + * setup(builder) { + * builder.onStart(() => { + * console.log("bundle just started!!") + * }); + * }, + * }); + * ``` + */ + onStart(callback: OnStartCallback): void; /** * The config object passed to `Bun.build` as is. Can be mutated. */ diff --git a/src/bun.js/api/JSBundler.zig b/src/bun.js/api/JSBundler.zig index 6dd0884bffab1f..baaf02ba8fc7fc 100644 --- a/src/bun.js/api/JSBundler.zig +++ b/src/bun.js/api/JSBundler.zig @@ -44,6 +44,8 @@ const JSLexer = bun.js_lexer; const Expr = JSAst.Expr; const Index = @import("../../ast/base.zig").Index; +const debug = bun.Output.scoped(.Bundler, false); + pub const JSBundler = struct { const OwnedString = bun.MutableString; @@ -113,8 +115,11 @@ pub const JSBundler = struct { // Plugins must be resolved first as they are allowed to mutate the config JSValue if (try config.getArray(globalThis, "plugins")) |array| { + const length = array.getLength(globalThis); var iter = array.arrayIterator(globalThis); - while (iter.next()) |plugin| { + var onstart_promise_array: JSValue = JSValue.undefined; + var i: usize = 0; + while (iter.next()) |plugin| : (i += 1) { if (!plugin.isObject()) { globalThis.throwInvalidArguments("Expected plugin to be an object", .{}); return error.JSError; @@ -148,19 +153,34 @@ pub const JSBundler = struct { break :brk plugins.*.?; }; - var plugin_result = bun_plugins.addPlugin(function, config); + const is_last = i == length - 1; + var plugin_result = try bun_plugins.addPlugin(function, config, onstart_promise_array, is_last); if (!plugin_result.isEmptyOrUndefinedOrNull()) { if (plugin_result.asAnyPromise()) |promise| { + promise.setHandled(globalThis.vm()); globalThis.bunVM().waitForPromise(promise); - plugin_result = promise.result(globalThis.vm()); + switch (promise.unwrap(globalThis.vm(), .mark_handled)) { + .pending => unreachable, + .fulfilled => |val| { + plugin_result = val; + }, + .rejected => |err| { + globalThis.throwValue(err); + return error.JSError; + }, + } } } if (plugin_result.toError()) |err| { globalThis.throwValue(err); return error.JSError; + } else if (globalThis.hasException()) { + return error.JSError; } + + onstart_promise_array = plugin_result; } } @@ -782,6 +802,8 @@ pub const JSBundler = struct { } }; + const DeferredTask = bun.bundle_v2.DeferredTask; + pub const Load = struct { source_index: Index, default_loader: options.Loader, @@ -799,6 +821,11 @@ pub const JSBundler = struct { /// Faster path: skip the extra threadpool dispatch when the file is not found was_file: bool = false, + // We only allow the user to call defer once right now + called_defer: bool = false, + + const debug_deferred = bun.Output.scoped(.BUNDLER_DEFERRED, true); + pub fn create( completion: *bun.BundleV2.JSBundleCompletionTask, source_index: Index, @@ -847,6 +874,7 @@ pub const JSBundler = struct { }; pub fn deinit(this: *Load) void { + debug("Deinit Load(0{x}, {s})", .{ @intFromPtr(this), this.path }); this.value.deinit(); if (this.completion) |completion| completion.deref(); @@ -855,7 +883,7 @@ pub const JSBundler = struct { const AnyTask = JSC.AnyTask.New(@This(), runOnJSThread); pub fn runOnJSThread(this: *Load) void { - var completion = this.completion orelse { + var completion: *bun.BundleV2.JSBundleCompletionTask = this.completion orelse { this.deinit(); return; }; @@ -870,7 +898,7 @@ pub const JSBundler = struct { } pub fn dispatch(this: *Load) void { - var completion = this.completion orelse { + var completion: *bun.BundleV2.JSBundleCompletionTask = this.completion orelse { this.deinit(); return; }; @@ -881,6 +909,35 @@ pub const JSBundler = struct { completion.jsc_event_loop.enqueueTaskConcurrent(concurrent_task); } + export fn JSBundlerPlugin__onDefer( + this: *Load, + globalObject: *JSC.JSGlobalObject, + ) JSValue { + if (this.called_defer) { + globalObject.throw("can't call .defer() more than once within an onLoad plugin", .{}); + return .undefined; + } + this.called_defer = true; + + _ = this.parse_task.ctx.graph.deferred_pending.fetchAdd(1, .acq_rel); + _ = @atomicRmw(usize, &this.parse_task.ctx.graph.parse_pending, .Sub, 1, .acq_rel); + + debug_deferred("JSBundlerPlugin__onDefer(0x{x}, {s}) parse_pending={d} deferred_pending={d}", .{ + @intFromPtr(this), + this.path, + @atomicLoad( + usize, + &this.parse_task.ctx.graph.parse_pending, + .monotonic, + ), + this.parse_task.ctx.graph.deferred_pending.load(.monotonic), + }); + + defer this.parse_task.ctx.loop().wakeup(); + const promise: JSValue = if (this.completion) |c| c.plugins.?.appendDeferPromise() else return .undefined; + return promise; + } + export fn JSBundlerPlugin__onLoadAsync( this: *Load, _: *anyopaque, @@ -888,7 +945,7 @@ pub const JSBundler = struct { loader_as_int: JSValue, ) void { JSC.markBinding(@src()); - var completion = this.completion orelse { + var completion: *bun.BundleV2.JSBundleCompletionTask = this.completion orelse { this.deinit(); return; }; @@ -902,12 +959,13 @@ pub const JSBundler = struct { return; } } else { + const loader: Api.Loader = @enumFromInt(loader_as_int.to(u8)); const source_code = JSC.Node.StringOrBuffer.fromJSToOwnedSlice(completion.globalThis, source_code_value, bun.default_allocator) catch // TODO: @panic("Unexpected: source_code is not a string"); this.value = .{ .success = .{ - .loader = @as(options.Loader, @enumFromInt(@as(u8, @intCast(loader_as_int.to(i32))))), + .loader = options.Loader.fromAPI(loader), .source_code = source_code, }, }; @@ -958,6 +1016,13 @@ pub const JSBundler = struct { u8, ) void; + extern fn JSBundlerPlugin__drainDeferred(*Plugin, rejected: bool) void; + extern fn JSBundlerPlugin__appendDeferPromise(*Plugin, rejected: bool) JSValue; + + pub fn appendDeferPromise(this: *Plugin) JSValue { + return JSBundlerPlugin__appendDeferPromise(this, false); + } + pub fn hasAnyMatches( this: *Plugin, path: *const Fs.Path, @@ -988,6 +1053,7 @@ pub const JSBundler = struct { JSC.markBinding(@src()); const tracer = bun.tracy.traceNamed(@src(), "JSBundler.matchOnLoad"); defer tracer.end(); + debug("JSBundler.matchOnLoad(0x{x}, {s}, {s})", .{ @intFromPtr(this), namespace, path }); const namespace_string = if (namespace.len == 0) bun.String.static("file") else @@ -1026,11 +1092,19 @@ pub const JSBundler = struct { this: *Plugin, object: JSC.JSValue, config: JSC.JSValue, - ) JSValue { + onstart_promises_array: JSC.JSValue, + is_last: bool, + ) !JSValue { JSC.markBinding(@src()); const tracer = bun.tracy.traceNamed(@src(), "JSBundler.addPlugin"); defer tracer.end(); - return JSBundlerPlugin__runSetupFunction(this, object, config); + const value = JSBundlerPlugin__runSetupFunction(this, object, config, onstart_promises_array, JSValue.jsBoolean(is_last)); + if (value == .zero) return error.JSError; + return value; + } + + pub fn drainDeferred(this: *Plugin, rejected: bool) void { + JSBundlerPlugin__drainDeferred(this, rejected); } pub fn deinit(this: *Plugin) void { @@ -1050,6 +1124,8 @@ pub const JSBundler = struct { *Plugin, JSC.JSValue, JSC.JSValue, + JSC.JSValue, + JSC.JSValue, ) JSValue; pub export fn JSBundlerPlugin__addError( diff --git a/src/bun.js/bindings/JSBundlerPlugin.cpp b/src/bun.js/bindings/JSBundlerPlugin.cpp index 2d5f0e7fce9c16..ff48b069180ce7 100644 --- a/src/bun.js/bindings/JSBundlerPlugin.cpp +++ b/src/bun.js/bindings/JSBundlerPlugin.cpp @@ -23,20 +23,25 @@ #include #include #include +#include +#include namespace Bun { #define WRAP_BUNDLER_PLUGIN(argName) jsNumber(bitwise_cast(reinterpret_cast(argName))) #define UNWRAP_BUNDLER_PLUGIN(callFrame) reinterpret_cast(bitwise_cast(callFrame->argument(0).asDouble())) +/// These are callbacks defined in Zig and to be run after their associated JS version is run extern "C" void JSBundlerPlugin__addError(void*, void*, JSC::EncodedJSValue, JSC::EncodedJSValue); extern "C" void JSBundlerPlugin__onLoadAsync(void*, void*, JSC::EncodedJSValue, JSC::EncodedJSValue); extern "C" void JSBundlerPlugin__onResolveAsync(void*, void*, JSC::EncodedJSValue, JSC::EncodedJSValue, JSC::EncodedJSValue); extern "C" void JSBundlerPlugin__onVirtualModulePlugin(void*, void*, JSC::EncodedJSValue, JSC::EncodedJSValue, JSC::EncodedJSValue); +extern "C" JSC::EncodedJSValue JSBundlerPlugin__onDefer(void*, JSC::JSGlobalObject*); JSC_DECLARE_HOST_FUNCTION(jsBundlerPluginFunction_addFilter); JSC_DECLARE_HOST_FUNCTION(jsBundlerPluginFunction_addError); JSC_DECLARE_HOST_FUNCTION(jsBundlerPluginFunction_onLoadAsync); JSC_DECLARE_HOST_FUNCTION(jsBundlerPluginFunction_onResolveAsync); +JSC_DECLARE_HOST_FUNCTION(jsBundlerPluginFunction_generateDeferPromise); void BundlerPlugin::NamespaceList::append(JSC::VM& vm, JSC::RegExp* filter, String& namespaceString) { @@ -111,6 +116,7 @@ static const HashTableValue JSBundlerPluginHashTable[] = { { "addError"_s, static_cast(JSC::PropertyAttribute::Function | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBundlerPluginFunction_addError, 3 } }, { "onLoadAsync"_s, static_cast(JSC::PropertyAttribute::Function | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBundlerPluginFunction_onLoadAsync, 3 } }, { "onResolveAsync"_s, static_cast(JSC::PropertyAttribute::Function | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBundlerPluginFunction_onResolveAsync, 4 } }, + { "generateDeferPromise"_s, static_cast(JSC::PropertyAttribute::Function | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBundlerPluginFunction_generateDeferPromise, 0 } }, }; class JSBundlerPlugin final : public JSC::JSNonFinalObject { @@ -153,6 +159,7 @@ class JSBundlerPlugin final : public JSC::JSNonFinalObject { DECLARE_VISIT_CHILDREN; Bun::BundlerPlugin plugin; + /// These are the user implementation of the plugin callbacks JSC::LazyProperty onLoadFunction; JSC::LazyProperty onResolveFunction; JSC::LazyProperty moduleFunction; @@ -249,6 +256,23 @@ JSC_DEFINE_HOST_FUNCTION(jsBundlerPluginFunction_onResolveAsync, (JSC::JSGlobalO return JSC::JSValue::encode(JSC::jsUndefined()); } +extern "C" JSC::EncodedJSValue JSBundlerPlugin__appendDeferPromise(Bun::JSBundlerPlugin* pluginObject, bool rejected) +{ + JSC::JSGlobalObject* globalObject = pluginObject->globalObject(); + Strong strong_promise = JSC::Strong(globalObject->vm(), JSPromise::create(globalObject->vm(), globalObject->promiseStructure())); + JSPromise* ret = strong_promise.get(); + pluginObject->plugin.deferredPromises.append(strong_promise); + + return JSC::JSValue::encode(ret); +} + +JSC_DEFINE_HOST_FUNCTION(jsBundlerPluginFunction_generateDeferPromise, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSBundlerPlugin* plugin = (JSBundlerPlugin*)UNWRAP_BUNDLER_PLUGIN(callFrame); + JSC::EncodedJSValue encoded_defer_promise = JSBundlerPlugin__onDefer(plugin, globalObject); + return encoded_defer_promise; +} + void JSBundlerPlugin::finishCreation(JSC::VM& vm) { Base::finishCreation(vm); @@ -386,7 +410,9 @@ extern "C" Bun::JSBundlerPlugin* JSBundlerPlugin__create(Zig::GlobalObject* glob extern "C" JSC::EncodedJSValue JSBundlerPlugin__runSetupFunction( Bun::JSBundlerPlugin* plugin, JSC::EncodedJSValue encodedSetupFunction, - JSC::EncodedJSValue encodedConfig) + JSC::EncodedJSValue encodedConfig, + JSC::EncodedJSValue encodedOnstartPromisesArray, + JSC::EncodedJSValue encodedIsLast) { auto& vm = plugin->vm(); auto scope = DECLARE_CATCH_SCOPE(vm); @@ -402,16 +428,11 @@ extern "C" JSC::EncodedJSValue JSBundlerPlugin__runSetupFunction( MarkedArgumentBuffer arguments; arguments.append(JSValue::decode(encodedSetupFunction)); arguments.append(JSValue::decode(encodedConfig)); + arguments.append(JSValue::decode(encodedOnstartPromisesArray)); + arguments.append(JSValue::decode(encodedIsLast)); auto* lexicalGlobalObject = jsCast(JSValue::decode(encodedSetupFunction))->globalObject(); - auto result = call(lexicalGlobalObject, setupFunction, callData, plugin, arguments); - if (UNLIKELY(scope.exception())) { - auto exception = scope.exception(); - scope.clearException(); - return JSValue::encode(exception); - } - - return JSValue::encode(result); + return JSC::JSValue::encode(JSC::call(lexicalGlobalObject, setupFunction, callData, plugin, arguments)); } extern "C" void JSBundlerPlugin__setConfig(Bun::JSBundlerPlugin* plugin, void* config) @@ -419,6 +440,19 @@ extern "C" void JSBundlerPlugin__setConfig(Bun::JSBundlerPlugin* plugin, void* c plugin->plugin.config = config; } +extern "C" void JSBundlerPlugin__drainDeferred(Bun::JSBundlerPlugin* pluginObject, bool rejected) +{ + auto deferredPromises = std::exchange(pluginObject->plugin.deferredPromises, {}); + for (auto& promise : deferredPromises) { + if (rejected) { + promise->reject(pluginObject->globalObject(), JSC::jsUndefined()); + } else { + promise->resolve(pluginObject->globalObject(), JSC::jsUndefined()); + } + promise.clear(); + } +} + extern "C" void JSBundlerPlugin__tombestone(Bun::JSBundlerPlugin* plugin) { plugin->plugin.tombstone(); diff --git a/src/bun.js/bindings/JSBundlerPlugin.h b/src/bun.js/bindings/JSBundlerPlugin.h index ca0d9f6c96766e..3f363bf41d5079 100644 --- a/src/bun.js/bindings/JSBundlerPlugin.h +++ b/src/bun.js/bindings/JSBundlerPlugin.h @@ -7,7 +7,6 @@ #include #include "helpers.h" #include -#include typedef void (*JSBundlerPluginAddErrorCallback)(void*, void*, JSC::EncodedJSValue, JSC::EncodedJSValue); typedef void (*JSBundlerPluginOnLoadAsyncCallback)(void*, void*, JSC::EncodedJSValue, JSC::EncodedJSValue); @@ -62,6 +61,8 @@ class BundlerPlugin final { NamespaceList onResolve = {}; BunPluginTarget target { BunPluginTargetBrowser }; + Vector> deferredPromises = {}; + JSBundlerPluginAddErrorCallback addError; JSBundlerPluginOnLoadAsyncCallback onLoadAsync; JSBundlerPluginOnResolveAsyncCallback onResolveAsync; diff --git a/src/bun.js/event_loop.zig b/src/bun.js/event_loop.zig index cd3d8a39aec256..6090bc6c9b0e82 100644 --- a/src/bun.js/event_loop.zig +++ b/src/bun.js/event_loop.zig @@ -484,6 +484,7 @@ pub const Task = TaggedPointerUnion(.{ RuntimeTranspilerStore, ServerAllConnectionsClosedTask, bun.bake.DevServer.HotReloadTask, + bun.bundle_v2.DeferredBatchTask, }); const UnboundedQueue = @import("./unbounded_queue.zig").UnboundedQueue; pub const ConcurrentTask = struct { @@ -1251,6 +1252,10 @@ pub const EventLoop = struct { var any: *ServerAllConnectionsClosedTask = task.get(ServerAllConnectionsClosedTask).?; any.runFromJSThread(virtual_machine); }, + @field(Task.Tag, typeBaseName(@typeName(bun.bundle_v2.DeferredBatchTask))) => { + var any: *bun.bundle_v2.DeferredBatchTask = task.get(bun.bundle_v2.DeferredBatchTask).?; + any.runOnJSThread(); + }, else => { bun.Output.panic("Unexpected tag: {s}", .{@tagName(task.tag())}); diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig index b7cdfd295cd9e7..9e5dfb43787384 100644 --- a/src/bundler/bundle_v2.zig +++ b/src/bundler/bundle_v2.zig @@ -104,7 +104,7 @@ const ThisBundler = @import("../bundler.zig").Bundler; const Dependency = js_ast.Dependency; const JSAst = js_ast.BundledAst; const Loader = options.Loader; -const Index = @import("../ast/base.zig").Index; +pub const Index = @import("../ast/base.zig").Index; const Batcher = bun.Batcher; const Symbol = js_ast.Symbol; const EventLoop = bun.JSC.AnyEventLoop; @@ -129,6 +129,8 @@ const Async = bun.Async; const Loc = Logger.Loc; const bake = bun.bake; +const debug_deferred = bun.Output.scoped(.BUNDLER_DEFERRED, true); + const logPartDependencyTree = Output.scoped(.part_dep_tree, false); fn tracer(comptime src: std.builtin.SourceLocation, comptime name: [:0]const u8) bun.tracy.Ctx { @@ -374,6 +376,8 @@ pub const BundleV2 = struct { unique_key: u64 = 0, dynamic_import_entry_points: std.AutoArrayHashMap(Index.Int, void) = undefined, + drain_defer_task: DeferredBatchTask = .{}, + const BakeOptions = struct { framework: bake.Framework, client_bundler: *Bundler, @@ -562,7 +566,13 @@ pub const BundleV2 = struct { } fn isDone(this: *BundleV2) bool { - return @atomicLoad(usize, &this.graph.parse_pending, .monotonic) == 0 and @atomicLoad(usize, &this.graph.resolve_pending, .monotonic) == 0; + if (@atomicLoad(usize, &this.graph.parse_pending, .acquire) == 0 and @atomicLoad(usize, &this.graph.resolve_pending, .monotonic) == 0) { + if (this.graph.drainDeferredTasks(this) > 0) { + return false; + } + return true; + } + return false; } pub fn waitForParse(this: *BundleV2) void { @@ -1635,6 +1645,7 @@ pub const BundleV2 = struct { pub fn deref(this: *JSBundleCompletionTask) void { if (this.ref_count.fetchSub(1, .monotonic) == 1) { this.config.deinit(bun.default_allocator); + debug("Deinit JSBundleCompletionTask(0{x})", .{@intFromPtr(this)}); bun.default_allocator.destroy(this); } } @@ -2932,7 +2943,7 @@ pub const BundleV2 = struct { this.bundler.log.addErrorFmt( null, Logger.Loc.Empty, - bun.default_allocator, + this.bundler.log.msgs.allocator, "{s} while {s}", .{ @errorName(err.err), @tagName(err.step) }, ) catch unreachable; @@ -3129,16 +3140,66 @@ pub fn BundleThread(CompletionStruct: type) type { const UseDirective = js_ast.UseDirective; const ServerComponentBoundary = js_ast.ServerComponentBoundary; +/// This task is run once all parse and resolve tasks have been complete +/// and we have deferred onLoad plugins that we need to resume +/// +/// It enqueues a task to be run on the JS thread which resolves the promise +/// for every onLoad callback which called `.defer()`. +pub const DeferredBatchTask = struct { + running: if (Environment.isDebug) bool else u0 = if (Environment.isDebug) false else 0, + + const AnyTask = JSC.AnyTask.New(@This(), runOnJSThread); + + pub fn init(this: *DeferredBatchTask) void { + if (comptime Environment.isDebug) bun.debugAssert(!this.running); + this.* = .{ + .running = if (comptime Environment.isDebug) false else 0, + }; + } + + pub fn getCompletion(this: *DeferredBatchTask) ?*bun.BundleV2.JSBundleCompletionTask { + const bundler: *BundleV2 = @alignCast(@fieldParentPtr("drain_defer_task", this)); + return bundler.completion; + } + + pub fn schedule(this: *DeferredBatchTask) void { + if (comptime Environment.isDebug) { + bun.assert(!this.running); + this.running = false; + } + this.getCompletion().?.jsc_event_loop.enqueueTaskConcurrent(JSC.ConcurrentTask.create(JSC.Task.init(this))); + } + + pub fn deinit(this: *DeferredBatchTask) void { + if (comptime Environment.isDebug) { + this.running = false; + } + } + + pub fn runOnJSThread(this: *DeferredBatchTask) void { + defer this.deinit(); + var completion: *bun.BundleV2.JSBundleCompletionTask = this.getCompletion() orelse { + return; + }; + + completion.bundler.plugins.?.drainDeferred(completion.result == .err); + } +}; + +const ContentsOrFd = union(Tag) { + fd: struct { + dir: StoredFileDescriptorType, + file: StoredFileDescriptorType, + }, + contents: string, + + const Tag = enum { fd, contents }; +}; + pub const ParseTask = struct { path: Fs.Path, secondary_path_for_commonjs_interop: ?Fs.Path = null, - contents_or_fd: union(enum) { - fd: struct { - dir: StoredFileDescriptorType, - file: StoredFileDescriptorType, - }, - contents: string, - }, + contents_or_fd: ContentsOrFd, side_effects: _resolver.SideEffects, loader: ?Loader = null, jsx: options.JSX.Pragma, @@ -3549,11 +3610,10 @@ pub const ParseTask = struct { if (bundler.options.experimental_css) { // const unique_key = std.fmt.allocPrint(allocator, "{any}A{d:0>8}", .{ bun.fmt.hexIntLower(unique_key_prefix), source.index.get() }) catch unreachable; // unique_key_for_additional_file.* = unique_key; - const root = Expr.init(E.Object, E.Object{}, Logger.Loc{ .start = 0 }); var import_records = BabyList(ImportRecord){}; const source_code = source.contents; var css_ast = - switch (bun.css.StyleSheet(bun.css.DefaultAtRule).parseBundler( + switch (bun.css.BundlerStyleSheet.parseBundler( allocator, source_code, bun.css.ParserOptions.default(allocator, bundler.log), @@ -3561,7 +3621,15 @@ pub const ParseTask = struct { )) { .result => |v| v, .err => |e| { - log.addErrorFmt(&source, Logger.Loc.Empty, allocator, "{?}: {}", .{ if (e.loc) |l| l.withFilename(source.path.pretty) else null, e.kind }) catch unreachable; + log.addErrorFmt( + &source, + if (e.loc) |loc| Logger.Loc{ + .start = @intCast(loc.line), + } else Logger.Loc.Empty, + allocator, + "{}", + .{e.kind}, + ) catch unreachable; return error.SyntaxError; }, }; @@ -3569,9 +3637,18 @@ pub const ParseTask = struct { .targets = .{}, .unused_symbols = .{}, }).asErr()) |e| { - log.addErrorFmt(&source, Logger.Loc.Empty, allocator, "{?}: {}", .{ if (e.loc) |l| l.withFilename(source.path.pretty) else null, e.kind }) catch unreachable; + log.addErrorFmt( + &source, + if (e.loc) |loc| Logger.Loc{ + .start = @intCast(loc.line), + } else Logger.Loc.Empty, + allocator, + "{}", + .{e.kind}, + ) catch unreachable; return error.MinifyError; } + const root = Expr.init(E.Object, E.Object{}, Logger.Loc{ .start = 0 }); const css_ast_heap = bun.create(allocator, bun.css.BundlerStyleSheet, css_ast); var ast = JSAst.init((try js_parser.newLazyExportAST(allocator, bundler.options.define, opts, log, root, &source, "")).?); ast.css = css_ast_heap; @@ -3676,7 +3753,31 @@ pub const ParseTask = struct { }, }; - errdefer if (task.contents_or_fd == .fd) entry.deinit(allocator); + // WARNING: Do not change the variant of `task.contents_or_fd` from + // `.fd` to `.contents` (or back) after this point! + // + // When `task.contents_or_fd == .fd`, `entry.contents` is an owned string. + // When `task.contents_or_fd == .contents`, `entry.contents` is NOT owned! Freeing it here will cause a double free! + // + // Changing from `.contents` to `.fd` will cause a double free. + // This was the case in the situation where the ParseTask receives its `.contents` from an onLoad plugin, which caused it to be + // allocated by `bun.default_allocator` and then freed in `BundleV2.deinit` (and also by `entry.deinit(allocator)` below). + const debug_original_variant_check: if (bun.Environment.isDebug) ContentsOrFd.Tag else u0 = + if (bun.Environment.isDebug) + @as(ContentsOrFd.Tag, task.contents_or_fd) + else + 0; + errdefer { + if (comptime bun.Environment.isDebug) { + if (@as(ContentsOrFd.Tag, task.contents_or_fd) != debug_original_variant_check) { + std.debug.panic("BUG: `task.contents_or_fd` changed in a way that will cause a double free or memory to leak!\n\n Original = {s}\n New = {s}\n", .{ + @tagName(debug_original_variant_check), + @tagName(task.contents_or_fd), + }); + } + } + if (task.contents_or_fd == .fd) entry.deinit(allocator); + } const will_close_file_descriptor = task.contents_or_fd == .fd and entry.fd.isValid() and !entry.fd.isStdio() and @@ -3684,7 +3785,7 @@ pub const ParseTask = struct { if (will_close_file_descriptor) { _ = entry.closeFD(); task.contents_or_fd = .{ .fd = .{ .file = bun.invalid_fd, .dir = bun.invalid_fd } }; - } else { + } else if (task.contents_or_fd == .fd) { task.contents_or_fd = .{ .fd = .{ .file = entry.fd, .dir = bun.invalid_fd, @@ -3827,6 +3928,7 @@ pub const ParseTask = struct { const this: *ParseTask = @fieldParentPtr("task", task); var worker = ThreadPool.Worker.get(this.ctx); defer worker.unget(); + debug("ParseTask(0x{x}, {s}) callback", .{ @intFromPtr(this), this.path.text }); var step: ParseTask.Result.Error.Step = .pending; var log = Logger.Log.init(worker.allocator); @@ -4301,6 +4403,9 @@ pub const Graph = struct { // using u32, since Ref does not support addressing sources above maxInt(u31) parse_pending: usize = 0, resolve_pending: usize = 0, + /// This is incremented whenever an onLoad plugin calls `.defer()` + /// And then is correspondingly decremented whenever we resume that onLoad plugin + deferred_pending: std.atomic.Value(usize) = .{ .raw = 0 }, /// Maps a hashed path string to a source index, if it exists in the compilation. /// Instead of accessing this directly, consider using BundleV2.pathToSourceIndexMap @@ -4347,6 +4452,21 @@ pub const Graph = struct { unique_key_for_additional_file: string = "", content_hash_for_additional_file: u64 = 0, }; + + /// Schedule a task to be run on the JS thread which resolves the promise of each `.defer()` called in an + /// onLoad plugin. + /// + /// Returns the amount of deferred tasks to resume. + pub fn drainDeferredTasks(this: *@This(), bundler: *BundleV2) usize { + const pending_deferred = this.deferred_pending.swap(0, .acq_rel); + if (pending_deferred > 0) { + _ = @atomicRmw(usize, &this.parse_pending, .Add, pending_deferred, .acq_rel); + bundler.drain_defer_task.init(); + bundler.drain_defer_task.schedule(); + return pending_deferred; + } + return pending_deferred; + } }; pub const AdditionalFile = union(enum) { @@ -5756,7 +5876,7 @@ pub const LinkerContext = struct { source_index, ) catch bun.outOfMemory(); - const repr: *const bun.css.BundlerStyleSheet = visitor.css_asts[source_index.get()].?; + const repr: *const bun.css.BundlerStyleSheet = visitor.css_asts[source_index.get()] orelse return; // Sanity check const top_level_rules = &repr.rules; // TODO: should we even do this? @import rules have to be the first rules in the stylesheet, why even allow pre-import layers? diff --git a/src/codegen/replacements.ts b/src/codegen/replacements.ts index 3d20dc4fa12e2b..ec09c14289f1cd 100644 --- a/src/codegen/replacements.ts +++ b/src/codegen/replacements.ts @@ -120,7 +120,7 @@ for (const name in enums) { if (typeof value === null) throw new Error("Invalid enum object " + name + " defined in " + import.meta.file); const keys = Array.isArray(value) ? value : Object.keys(value).filter(k => !k.match(/^[0-9]+$/)); define[`$${name}IdToLabel`] = "[" + keys.map(k => `"${k}"`).join(", ") + "]"; - define[`$${name}LabelToId`] = "{" + keys.map(k => `"${k}": ${keys.indexOf(k)}`).join(", ") + "}"; + define[`$${name}LabelToId`] = "{" + keys.map(k => `"${k}": ${keys.indexOf(k) + 1}`).join(", ") + "}"; } for (const name of globalsToPrefix) { diff --git a/src/css/css_parser.zig b/src/css/css_parser.zig index 8cdb95c4155ada..edac8510996994 100644 --- a/src/css/css_parser.zig +++ b/src/css/css_parser.zig @@ -32,6 +32,7 @@ pub const ImportRule = css_rules.import.ImportRule; pub const StyleRule = css_rules.style.StyleRule; pub const StyleContext = css_rules.StyleContext; pub const SupportsRule = css_rules.supports.SupportsRule; +pub const TailwindAtRule = css_rules.tailwind.TailwindAtRule; pub const MinifyContext = css_rules.MinifyContext; @@ -560,7 +561,7 @@ pub fn DeriveParse(comptime T: type) type { .err => |e| return .{ .err = e }, }; if (Map.getCaseInsensitiveWithEql(ident, bun.strings.eqlComptimeIgnoreLen)) |matched| { - inline for (bun.meta.EnumFields(enum_type)) |field| { + inline for (bun.meta.EnumFields(enum_actual_type)) |field| { if (field.value == @intFromEnum(matched)) { if (comptime is_union_enum) return .{ .result = @unionInit(T, field.name, void) }; return .{ .result = @enumFromInt(field.value) }; @@ -826,10 +827,11 @@ pub const enum_property_util = struct { .result => |v| v, }; - // todo_stuff.match_ignore_ascii_case - inline for (std.meta.fields(T)) |field| { - if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(ident, field.name)) return .{ .result = @enumFromInt(field.value) }; - } + const Map = comptime bun.ComptimeEnumMap(T); + if (Map.getASCIIICaseInsensitive(ident)) |x| return .{ .result = x }; + // inline for (std.meta.fields(T)) |field| { + // if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(ident, field.name)) return .{ .result = @enumFromInt(field.value) }; + // } return .{ .err = location.newUnexpectedTokenError(.{ .ident = ident }) }; } @@ -1289,16 +1291,49 @@ pub const DefaultAtRuleParser = struct { }; }; +/// We may want to enable this later +pub const ENABLE_TAILWIND_PARSING = false; + +pub const BundlerAtRule = if (ENABLE_TAILWIND_PARSING) TailwindAtRule else DefaultAtRule; pub const BundlerAtRuleParser = struct { const This = @This(); allocator: Allocator, import_records: *bun.BabyList(ImportRecord), + options: *const ParserOptions, pub const CustomAtRuleParser = struct { - pub const Prelude = void; - pub const AtRule = DefaultAtRule; - - pub fn parsePrelude(_: *This, name: []const u8, input: *Parser, _: *const ParserOptions) Result(Prelude) { + pub const Prelude = if (ENABLE_TAILWIND_PARSING) union(enum) { + tailwind: TailwindAtRule, + } else void; + pub const AtRule = if (ENABLE_TAILWIND_PARSING) TailwindAtRule else DefaultAtRule; + + pub fn parsePrelude(this: *This, name: []const u8, input: *Parser, _: *const ParserOptions) Result(Prelude) { + if (comptime ENABLE_TAILWIND_PARSING) { + const PreludeNames = enum { + tailwind, + }; + const Map = comptime bun.ComptimeEnumMap(PreludeNames); + if (Map.getASCIIICaseInsensitive(name)) |prelude| return switch (prelude) { + .tailwind => { + const loc_ = input.currentSourceLocation(); + const loc = css_rules.Location{ + .source_index = this.options.source_index, + .line = loc_.line, + .column = loc_.column, + }; + const style_name = switch (css_rules.tailwind.TailwindStyleName.parse(input)) { + .result => |v| v, + .err => return .{ .err = input.newError(BasicParseErrorKind{ .at_rule_invalid = name }) }, + }; + return .{ .result = .{ + .tailwind = .{ + .style_name = style_name, + .loc = loc, + }, + } }; + }, + }; + } return .{ .err = input.newError(BasicParseErrorKind{ .at_rule_invalid = name }) }; } @@ -1306,7 +1341,12 @@ pub const BundlerAtRuleParser = struct { return .{ .err = input.newError(BasicParseErrorKind.at_rule_body_invalid) }; } - pub fn ruleWithoutBlock(_: *This, _: CustomAtRuleParser.Prelude, _: *const ParserState, _: *const ParserOptions, _: bool) Maybe(CustomAtRuleParser.AtRule, void) { + pub fn ruleWithoutBlock(_: *This, prelude: CustomAtRuleParser.Prelude, _: *const ParserState, _: *const ParserOptions, _: bool) Maybe(CustomAtRuleParser.AtRule, void) { + if (comptime ENABLE_TAILWIND_PARSING) { + return switch (prelude) { + .tailwind => |v| return .{ .result = v }, + }; + } return .{ .err = {} }; } @@ -1517,104 +1557,121 @@ pub fn TopLevelRuleParser(comptime AtRuleParserT: type) type { pub const AtRule = void; pub fn parsePrelude(this: *This, name: []const u8, input: *Parser) Result(Prelude) { - if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "import")) { - if (@intFromEnum(this.state) > @intFromEnum(State.imports)) { - return .{ .err = input.newCustomError(@as(ParserError, ParserError.unexpected_import_rule)) }; - } - const url_str = switch (input.expectUrlOrString()) { - .err => |e| return .{ .err = e }, - .result => |v| v, - }; + const PreludeEnum = enum { + import, + charset, + namespace, + @"custom-media", + property, + }; + const Map = comptime bun.ComptimeEnumMap(PreludeEnum); - const layer: ?struct { value: ?LayerName } = - if (input.tryParse(Parser.expectIdentMatching, .{"layer"}) == .result) - .{ .value = null } - else if (input.tryParse(Parser.expectFunctionMatching, .{"layer"}) == .result) brk: { - break :brk .{ - .value = switch (input.parseNestedBlock(LayerName, {}, voidWrap(LayerName, LayerName.parse))) { - .result => |v| v, - .err => |e| return .{ .err = e }, - }, - }; - } else null; - - const supports = if (input.tryParse(Parser.expectFunctionMatching, .{"supports"}) == .result) brk: { - const Func = struct { - pub fn do(_: void, p: *Parser) Result(SupportsCondition) { - const result = p.tryParse(SupportsCondition.parse, .{}); - if (result == .err) return SupportsCondition.parseDeclaration(p); - return result; + if (Map.getASCIIICaseInsensitive(name)) |prelude| { + switch (prelude) { + .import => { + if (@intFromEnum(this.state) > @intFromEnum(State.imports)) { + return .{ .err = input.newCustomError(@as(ParserError, ParserError.unexpected_import_rule)) }; } - }; - break :brk switch (input.parseNestedBlock(SupportsCondition, {}, Func.do)) { - .result => |v| v, - .err => |e| return .{ .err = e }, - }; - } else null; + const url_str = switch (input.expectUrlOrString()) { + .err => |e| return .{ .err = e }, + .result => |v| v, + }; - const media = switch (MediaList.parse(input)) { - .err => |e| return .{ .err = e }, - .result => |v| v, - }; + const layer: ?struct { value: ?LayerName } = + if (input.tryParse(Parser.expectIdentMatching, .{"layer"}) == .result) + .{ .value = null } + else if (input.tryParse(Parser.expectFunctionMatching, .{"layer"}) == .result) brk: { + break :brk .{ + .value = switch (input.parseNestedBlock(LayerName, {}, voidWrap(LayerName, LayerName.parse))) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }, + }; + } else null; + + const supports = if (input.tryParse(Parser.expectFunctionMatching, .{"supports"}) == .result) brk: { + const Func = struct { + pub fn do(_: void, p: *Parser) Result(SupportsCondition) { + const result = p.tryParse(SupportsCondition.parse, .{}); + if (result == .err) return SupportsCondition.parseDeclaration(p); + return result; + } + }; + break :brk switch (input.parseNestedBlock(SupportsCondition, {}, Func.do)) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + } else null; - return .{ - .result = .{ - .import = .{ - url_str, - media, - supports, - if (layer) |l| .{ .value = if (l.value) |ll| ll else null } else null, - }, + const media = switch (MediaList.parse(input)) { + .err => |e| return .{ .err = e }, + .result => |v| v, + }; + + return .{ + .result = .{ + .import = .{ + url_str, + media, + supports, + if (layer) |l| .{ .value = if (l.value) |ll| ll else null } else null, + }, + }, + }; }, - }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "namespace")) { - if (@intFromEnum(this.state) > @intFromEnum(State.namespaces)) { - return .{ .err = input.newCustomError(ParserError{ .unexpected_namespace_rule = {} }) }; - } + .namespace => { + if (@intFromEnum(this.state) > @intFromEnum(State.namespaces)) { + return .{ .err = input.newCustomError(ParserError{ .unexpected_namespace_rule = {} }) }; + } - const prefix = switch (input.tryParse(Parser.expectIdent, .{})) { - .result => |v| v, - .err => null, - }; - const namespace = switch (input.expectUrlOrString()) { - .err => |e| return .{ .err = e }, - .result => |v| v, - }; - return .{ .result = .{ .namespace = .{ prefix, namespace } } }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "charset")) { - // @charset is removed by rust-cssparser if it’s the first rule in the stylesheet. - // Anything left is technically invalid, however, users often concatenate CSS files - // together, so we are more lenient and simply ignore @charset rules in the middle of a file. - if (input.expectString().asErr()) |e| return .{ .err = e }; - return .{ .result = .charset }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "custom-media")) { - const custom_media_name = switch (DashedIdentFns.parse(input)) { - .err => |e| return .{ .err = e }, - .result => |v| v, - }; - const media = switch (MediaList.parse(input)) { - .err => |e| return .{ .err = e }, - .result => |v| v, - }; - return .{ - .result = .{ - .custom_media = .{ - custom_media_name, - media, - }, + const prefix = switch (input.tryParse(Parser.expectIdent, .{})) { + .result => |v| v, + .err => null, + }; + const namespace = switch (input.expectUrlOrString()) { + .err => |e| return .{ .err = e }, + .result => |v| v, + }; + return .{ .result = .{ .namespace = .{ prefix, namespace } } }; }, - }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "property")) { - const property_name = switch (DashedIdentFns.parse(input)) { - .err => |e| return .{ .err = e }, - .result => |v| v, - }; - return .{ .result = .{ .property = .{property_name} } }; - } else { - const Nested = NestedRuleParser(AtRuleParserT); - var nested_rule_parser: Nested = this.nested(); - return Nested.AtRuleParser.parsePrelude(&nested_rule_parser, name, input); + .charset => { + // @charset is removed by rust-cssparser if it's the first rule in the stylesheet. + // Anything left is technically invalid, however, users often concatenate CSS files + // together, so we are more lenient and simply ignore @charset rules in the middle of a file. + if (input.expectString().asErr()) |e| return .{ .err = e }; + return .{ .result = .charset }; + }, + .@"custom-media" => { + const custom_media_name = switch (DashedIdentFns.parse(input)) { + .err => |e| return .{ .err = e }, + .result => |v| v, + }; + const media = switch (MediaList.parse(input)) { + .err => |e| return .{ .err = e }, + .result => |v| v, + }; + return .{ + .result = .{ + .custom_media = .{ + custom_media_name, + media, + }, + }, + }; + }, + .property => { + const property_name = switch (DashedIdentFns.parse(input)) { + .err => |e| return .{ .err = e }, + .result => |v| v, + }; + return .{ .result = .{ .property = .{property_name} } }; + }, + } } + + const Nested = NestedRuleParser(AtRuleParserT); + var nested_rule_parser: Nested = this.nested(); + return Nested.AtRuleParser.parsePrelude(&nested_rule_parser, name, input); } pub fn parseBlock(this: *This, prelude: AtRuleParser.Prelude, start: *const ParserState, input: *Parser) Result(AtRuleParser.AtRule) { @@ -1788,174 +1845,203 @@ pub fn NestedRuleParser(comptime T: type) type { pub fn parsePrelude(this: *This, name: []const u8, input: *Parser) Result(Prelude) { const result: Prelude = brk: { - if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "media")) { - const media = switch (MediaList.parse(input)) { - .err => |e| return .{ .err = e }, - .result => |v| v, - }; - break :brk .{ .media = media }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "supports")) { - const cond = switch (SupportsCondition.parse(input)) { - .err => |e| return .{ .err = e }, - .result => |v| v, - }; - break :brk .{ .supports = cond }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "font-face")) { - break :brk .font_face; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "font-palette-values")) { - const dashed_ident_name = switch (DashedIdentFns.parse(input)) { - .err => |e| return .{ .err = e }, - .result => |v| v, - }; - break :brk .{ .font_palette_values = dashed_ident_name }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "counter-style")) { - const custom_name = switch (CustomIdentFns.parse(input)) { - .err => |e| return .{ .err = e }, - .result => |v| v, - }; - break :brk .{ .counter_style = custom_name }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "viewport") or bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "-ms-viewport")) { - const prefix: VendorPrefix = if (bun.strings.startsWithCaseInsensitiveAscii(name, "-ms")) VendorPrefix{ .ms = true } else VendorPrefix{ .none = true }; - break :brk .{ .viewport = prefix }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "keyframes") or - bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "-ms-viewport") or - bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "-moz-keyframes") or - bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "-o-keyframes") or - bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "-ms-keyframes")) - { - const prefix: VendorPrefix = if (bun.strings.startsWithCaseInsensitiveAscii(name, "-webkit")) - VendorPrefix{ .webkit = true } - else if (bun.strings.startsWithCaseInsensitiveAscii(name, "-moz-")) - VendorPrefix{ .moz = true } - else if (bun.strings.startsWithCaseInsensitiveAscii(name, "-o-")) - VendorPrefix{ .o = true } - else if (bun.strings.startsWithCaseInsensitiveAscii(name, "-ms-")) VendorPrefix{ .ms = true } else VendorPrefix{ .none = true }; - - const keyframes_name = switch (input.tryParse(css_rules.keyframes.KeyframesName.parse, .{})) { - .err => |e| return .{ .err = e }, - .result => |v| v, - }; - break :brk .{ .keyframes = .{ .name = keyframes_name, .prefix = prefix } }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "page")) { - const Fn = struct { - pub fn parsefn(input2: *Parser) Result(ArrayList(css_rules.page.PageSelector)) { - return input2.parseCommaSeparated(css_rules.page.PageSelector, css_rules.page.PageSelector.parse); - } - }; - const selectors = switch (input.tryParse(Fn.parsefn, .{})) { - .result => |v| v, - .err => ArrayList(css_rules.page.PageSelector){}, - }; - break :brk .{ .page = selectors }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "-moz-document")) { - // Firefox only supports the url-prefix() function with no arguments as a legacy CSS hack. - // See https://css-tricks.com/snippets/css/css-hacks-targeting-firefox/ - if (input.expectFunctionMatching("url-prefix").asErr()) |e| return .{ .err = e }; - const Fn = struct { - pub fn parsefn(_: void, input2: *Parser) Result(void) { - // Firefox also allows an empty string as an argument... - // https://github.com/mozilla/gecko-dev/blob/0077f2248712a1b45bf02f0f866449f663538164/servo/components/style/stylesheets/document_rule.rs#L303 - _ = input2.tryParse(parseInner, .{}); - if (input2.expectExhausted().asErr()) |e| return .{ .err = e }; - return .{ .result = {} }; - } - fn parseInner(input2: *Parser) Result(void) { - const s = switch (input2.expectString()) { - .err => |e| return .{ .err = e }, - .result => |v| v, - }; - if (s.len > 0) { - return .{ .err = input2.newCustomError(ParserError.invalid_value) }; + const PreludeEnum = enum { + media, + supports, + @"font-face", + @"font-palette-values", + @"counter-style", + viewport, + keyframes, + @"-ms-viewport", + @"-moz-keyframes", + @"-o-keyframes", + @"-ms-keyframes", + page, + @"-moz-document", + layer, + container, + @"starting-style", + scope, + nest, + }; + const Map = comptime bun.ComptimeEnumMap(PreludeEnum); + if (Map.getASCIIICaseInsensitive(name)) |kind| switch (kind) { + .media => { + const media = switch (MediaList.parse(input)) { + .err => |e| return .{ .err = e }, + .result => |v| v, + }; + break :brk .{ .media = media }; + }, + .supports => { + const cond = switch (SupportsCondition.parse(input)) { + .err => |e| return .{ .err = e }, + .result => |v| v, + }; + break :brk .{ .supports = cond }; + }, + .@"font-face" => break :brk .font_face, + .@"font-palette-values" => { + const dashed_ident_name = switch (DashedIdentFns.parse(input)) { + .err => |e| return .{ .err = e }, + .result => |v| v, + }; + break :brk .{ .font_palette_values = dashed_ident_name }; + }, + .@"counter-style" => { + const custom_name = switch (CustomIdentFns.parse(input)) { + .err => |e| return .{ .err = e }, + .result => |v| v, + }; + break :brk .{ .counter_style = custom_name }; + }, + .viewport, .@"-ms-viewport" => { + const prefix: VendorPrefix = if (bun.strings.startsWithCaseInsensitiveAscii(name, "-ms")) VendorPrefix{ .ms = true } else VendorPrefix{ .none = true }; + break :brk .{ .viewport = prefix }; + }, + .keyframes, .@"-moz-keyframes", .@"-o-keyframes", .@"-ms-keyframes" => { + const prefix: VendorPrefix = if (bun.strings.startsWithCaseInsensitiveAscii(name, "-webkit")) + VendorPrefix{ .webkit = true } + else if (bun.strings.startsWithCaseInsensitiveAscii(name, "-moz-")) + VendorPrefix{ .moz = true } + else if (bun.strings.startsWithCaseInsensitiveAscii(name, "-o-")) + VendorPrefix{ .o = true } + else if (bun.strings.startsWithCaseInsensitiveAscii(name, "-ms-")) VendorPrefix{ .ms = true } else VendorPrefix{ .none = true }; + + const keyframes_name = switch (input.tryParse(css_rules.keyframes.KeyframesName.parse, .{})) { + .err => |e| return .{ .err = e }, + .result => |v| v, + }; + break :brk .{ .keyframes = .{ .name = keyframes_name, .prefix = prefix } }; + }, + .page => { + const Fn = struct { + pub fn parsefn(input2: *Parser) Result(ArrayList(css_rules.page.PageSelector)) { + return input2.parseCommaSeparated(css_rules.page.PageSelector, css_rules.page.PageSelector.parse); } - return .{ .result = {} }; - } - }; - if (input.parseNestedBlock(void, {}, Fn.parsefn).asErr()) |e| return .{ .err = e }; - break :brk .moz_document; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "layer")) { - const names = switch (input.parseList(LayerName, LayerName.parse)) { - .result => |vv| vv, - .err => |e| names: { - if (e.kind == .basic and e.kind.basic == .end_of_input) { - break :names ArrayList(LayerName){}; + }; + const selectors = switch (input.tryParse(Fn.parsefn, .{})) { + .result => |v| v, + .err => ArrayList(css_rules.page.PageSelector){}, + }; + break :brk .{ .page = selectors }; + }, + .@"-moz-document" => { + // Firefox only supports the url-prefix() function with no arguments as a legacy CSS hack. + // See https://css-tricks.com/snippets/css/css-hacks-targeting-firefox/ + if (input.expectFunctionMatching("url-prefix").asErr()) |e| return .{ .err = e }; + const Fn = struct { + pub fn parsefn(_: void, input2: *Parser) Result(void) { + // Firefox also allows an empty string as an argument... + // https://github.com/mozilla/gecko-dev/blob/0077f2248712a1b45bf02f0f866449f663538164/servo/components/style/stylesheets/document_rule.rs#L303 + _ = input2.tryParse(parseInner, .{}); + if (input2.expectExhausted().asErr()) |e| return .{ .err = e }; + return .{ .result = {} }; } - return .{ .err = e }; - }, - }; - - break :brk .{ .layer = names }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "container")) { - const container_name = switch (input.tryParse(css_rules.container.ContainerName.parse, .{})) { - .result => |vv| vv, - .err => null, - }; - const condition = switch (css_rules.container.ContainerCondition.parse(input)) { - .err => |e| return .{ .err = e }, - .result => |v| v, - }; - break :brk .{ .container = .{ .name = container_name, .condition = condition } }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "starting-style")) { - break :brk .starting_style; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "scope")) { - var selector_parser = selector.parser.SelectorParser{ - .is_nesting_allowed = true, - .options = this.options, - .allocator = input.allocator(), - }; - const Closure = struct { - selector_parser: *selector.parser.SelectorParser, - pub fn parsefn(self: *@This(), input2: *Parser) Result(selector.parser.SelectorList) { - return selector.parser.SelectorList.parseRelative(self.selector_parser, input2, .ignore_invalid_selector, .none); - } - }; - var closure = Closure{ - .selector_parser = &selector_parser, - }; + fn parseInner(input2: *Parser) Result(void) { + const s = switch (input2.expectString()) { + .err => |e| return .{ .err = e }, + .result => |v| v, + }; + if (s.len > 0) { + return .{ .err = input2.newCustomError(ParserError.invalid_value) }; + } + return .{ .result = {} }; + } + }; + if (input.parseNestedBlock(void, {}, Fn.parsefn).asErr()) |e| return .{ .err = e }; + break :brk .moz_document; + }, + .layer => { + const names = switch (input.parseList(LayerName, LayerName.parse)) { + .result => |vv| vv, + .err => |e| names: { + if (e.kind == .basic and e.kind.basic == .end_of_input) { + break :names ArrayList(LayerName){}; + } + return .{ .err = e }; + }, + }; - const scope_start = if (input.tryParse(Parser.expectParenthesisBlock, .{}).isOk()) scope_start: { - break :scope_start switch (input.parseNestedBlock(selector.parser.SelectorList, &closure, Closure.parsefn)) { - .result => |v| v, + break :brk .{ .layer = names }; + }, + .container => { + const container_name = switch (input.tryParse(css_rules.container.ContainerName.parse, .{})) { + .result => |vv| vv, + .err => null, + }; + const condition = switch (css_rules.container.ContainerCondition.parse(input)) { .err => |e| return .{ .err = e }, + .result => |v| v, + }; + break :brk .{ .container = .{ .name = container_name, .condition = condition } }; + }, + .@"starting-style" => break :brk .starting_style, + .scope => { + var selector_parser = selector.parser.SelectorParser{ + .is_nesting_allowed = true, + .options = this.options, + .allocator = input.allocator(), + }; + const Closure = struct { + selector_parser: *selector.parser.SelectorParser, + pub fn parsefn(self: *@This(), input2: *Parser) Result(selector.parser.SelectorList) { + return selector.parser.SelectorList.parseRelative(self.selector_parser, input2, .ignore_invalid_selector, .none); + } + }; + var closure = Closure{ + .selector_parser = &selector_parser, }; - } else null; - const scope_end = if (input.tryParse(Parser.expectIdentMatching, .{"to"}).isOk()) scope_end: { - if (input.expectParenthesisBlock().asErr()) |e| return .{ .err = e }; - break :scope_end switch (input.parseNestedBlock(selector.parser.SelectorList, &closure, Closure.parsefn)) { - .result => |v| v, - .err => |e| return .{ .err = e }, + const scope_start = if (input.tryParse(Parser.expectParenthesisBlock, .{}).isOk()) scope_start: { + break :scope_start switch (input.parseNestedBlock(selector.parser.SelectorList, &closure, Closure.parsefn)) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + } else null; + + const scope_end = if (input.tryParse(Parser.expectIdentMatching, .{"to"}).isOk()) scope_end: { + if (input.expectParenthesisBlock().asErr()) |e| return .{ .err = e }; + break :scope_end switch (input.parseNestedBlock(selector.parser.SelectorList, &closure, Closure.parsefn)) { + .result => |v| v, + .err => |e| return .{ .err = e }, + }; + } else null; + + break :brk .{ + .scope = .{ + .scope_start = scope_start, + .scope_end = scope_end, + }, }; - } else null; + }, + .nest => { + if (this.is_in_style_rule) { + this.options.warn(input.newCustomError(ParserError{ .deprecated_nest_rule = {} })); + var selector_parser = selector.parser.SelectorParser{ + .is_nesting_allowed = true, + .options = this.options, + .allocator = input.allocator(), + }; + const selectors = switch (selector.parser.SelectorList.parse(&selector_parser, input, .discard_list, .contained)) { + .err => |e| return .{ .err = e }, + .result => |v| v, + }; + break :brk .{ .nest = selectors }; + } + }, + }; - break :brk .{ - .scope = .{ - .scope_start = scope_start, - .scope_end = scope_end, - }, - }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name, "nest") and this.is_in_style_rule) { - this.options.warn(input.newCustomError(ParserError{ .deprecated_nest_rule = {} })); - var selector_parser = selector.parser.SelectorParser{ - .is_nesting_allowed = true, - .options = this.options, - .allocator = input.allocator(), - }; - const selectors = switch (selector.parser.SelectorList.parse(&selector_parser, input, .discard_list, .contained)) { - .err => |e| return .{ .err = e }, - .result => |v| v, - }; - break :brk .{ .nest = selectors }; - } else { - break :brk switch (parse_custom_at_rule_prelude( - name, - input, - this.options, - T, - this.at_rule_parser, - )) { - .result => |v| v, - .err => |e| return .{ .err = e }, - }; + switch (parse_custom_at_rule_prelude( + name, + input, + this.options, + T, + this.at_rule_parser, + )) { + .result => |v| break :brk v, + .err => |e| return .{ .err = e }, } }; @@ -2589,10 +2675,15 @@ pub const MinifyOptions = struct { } }; -pub const BundlerStyleSheet = StyleSheet(DefaultAtRule); -pub const BundlerCssRuleList = CssRuleList(DefaultAtRule); -pub const BundlerCssRule = CssRule(DefaultAtRule); -pub const BundlerLayerBlockRule = css_rules.layer.LayerBlockRule(DefaultAtRule); +pub const BundlerStyleSheet = StyleSheet(BundlerAtRule); +pub const BundlerCssRuleList = CssRuleList(BundlerAtRule); +pub const BundlerCssRule = CssRule(BundlerAtRule); +pub const BundlerLayerBlockRule = css_rules.layer.LayerBlockRule(BundlerAtRule); +pub const BundlerTailwindState = struct { + source: []const u8, + index: bun.bundle_v2.Index, + output_from_tailwind: ?[]const u8 = null, +}; pub fn StyleSheet(comptime AtRule: type) type { return struct { @@ -2602,6 +2693,7 @@ pub fn StyleSheet(comptime AtRule: type) type { source_map_urls: ArrayList(?[]const u8), license_comments: ArrayList([]const u8), options: ParserOptions, + tailwind: if (AtRule == BundlerAtRule) ?*BundlerTailwindState else u0 = if (AtRule == BundlerAtRule) null else 0, const This = @This(); @@ -2728,6 +2820,7 @@ pub fn StyleSheet(comptime AtRule: type) type { var at_rule_parser = BundlerAtRuleParser{ .import_records = import_records, .allocator = allocator, + .options = &options, }; return parseWith(allocator, code, options, BundlerAtRuleParser, &at_rule_parser, import_records); } @@ -2794,6 +2887,100 @@ pub fn StyleSheet(comptime AtRule: type) type { }, }; } + + pub fn containsTailwindDirectives(this: *const @This()) bool { + if (comptime AtRule != BundlerAtRule) @compileError("Expected BundlerAtRule for this function."); + var found_import: bool = false; + for (this.rules.v.items) |*rule| { + switch (rule.*) { + .custom => { + return true; + }, + // .charset => {}, + // TODO: layer + .layer_block => {}, + .import => { + found_import = true; + }, + else => { + return false; + }, + } + } + return false; + } + + pub fn newFromTailwindImports( + allocator: Allocator, + options: ParserOptions, + imports_from_tailwind: CssRuleList(AtRule), + ) @This() { + _ = allocator; // autofix + if (comptime AtRule != BundlerAtRule) @compileError("Expected BundlerAtRule for this function."); + + const stylesheet = This{ + .rules = imports_from_tailwind, + .sources = .{}, + .source_map_urls = .{}, + .license_comments = .{}, + .options = options, + }; + + return stylesheet; + } + + /// *NOTE*: Used for Tailwind stylesheets only + /// + /// This plucks out the import rules from the Tailwind stylesheet into a separate rule list, + /// replacing them with `.ignored` rules. + /// + /// We do this because Tailwind's compiler pipeline does not bundle imports, so we handle that + /// ourselves in the bundler. + pub fn pluckImports(this: *const @This(), allocator: Allocator, out: *CssRuleList(AtRule), new_import_records: *bun.BabyList(ImportRecord)) void { + if (comptime AtRule != BundlerAtRule) @compileError("Expected BundlerAtRule for this function."); + const State = enum { count, exec }; + + const STATES = comptime [_]State{ .count, .exec }; + + var count: u32 = 0; + inline for (STATES[0..]) |state| { + if (comptime state == .exec) { + out.v.ensureUnusedCapacity(allocator, count) catch bun.outOfMemory(); + } + var saw_imports = false; + for (this.rules.v.items) |*rule| { + switch (rule.*) { + // TODO: layer, might have imports + .layer_block => {}, + .import => { + if (!saw_imports) saw_imports = true; + switch (state) { + .count => count += 1, + .exec => { + const import_rule = &rule.import; + out.v.appendAssumeCapacity(rule.*); + const import_record_idx = new_import_records.len; + import_rule.import_record_idx = import_record_idx; + new_import_records.push(allocator, ImportRecord{ + .path = bun.fs.Path.init(import_rule.url), + .kind = if (import_rule.supports != null) .at_conditional else .at, + .range = bun.logger.Range.None, + }) catch bun.outOfMemory(); + rule.* = .ignored; + }, + } + }, + .unknown => { + if (bun.strings.eqlComptime(rule.unknown.name, "tailwind")) { + continue; + } + }, + else => {}, + } + if (saw_imports) break; + } + } + } }; } diff --git a/src/css/media_query.zig b/src/css/media_query.zig index 696968b4db060f..1f56da703ec1e8 100644 --- a/src/css/media_query.zig +++ b/src/css/media_query.zig @@ -43,7 +43,7 @@ pub fn ValidQueryCondition(comptime T: type) void { /// A [media query list](https://drafts.csswg.org/mediaqueries/#mq-list). pub const MediaList = struct { /// The list of media queries. - media_queries: ArrayList(MediaQuery), + media_queries: ArrayList(MediaQuery) = .{}, /// Parse a media query list from CSS. pub fn parse(input: *css.Parser) Result(MediaList) { diff --git a/src/css/properties/generate_properties.ts b/src/css/properties/generate_properties.ts index aa6853dd2bc5fa..e7f79c1e2e04d7 100644 --- a/src/css/properties/generate_properties.ts +++ b/src/css/properties/generate_properties.ts @@ -369,11 +369,21 @@ function generatePropertyIdImpl(property_defs: Record): str } pub fn fromNameAndPrefix(name1: []const u8, pre: VendorPrefix) ?PropertyId { - // TODO: todo_stuff.match_ignore_ascii_case - ${generatePropertyIdImplFromNameAndPrefix(property_defs)} - if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "all")) { - } else { - return null; + const Enum = enum { ${Object.entries(property_defs) + .map( + ([prop_name, def], i) => `${escapeIdent(prop_name)}${i === Object.keys(property_defs).length - 1 ? "" : ", "}`, + ) + .join("")} }; + const Map = comptime bun.ComptimeEnumMap(Enum); + if (Map.getASCIIICaseInsensitive(name1)) |prop| { + switch (prop) { + ${Object.entries(property_defs).map(([name, meta]) => { + return `.${escapeIdent(name)} => { + const allowed_prefixes = ${constructVendorPrefix(meta.valid_prefixes)}; + if (allowed_prefixes.contains(pre)) return ${meta.valid_prefixes === undefined ? `.${escapeIdent(name)}` : `.{ .${escapeIdent(name)} = pre }`}; + }`; + })} + } } return null; diff --git a/src/css/properties/properties_generated.zig b/src/css/properties/properties_generated.zig index e9e54869846934..be76ee77a7fc85 100644 --- a/src/css/properties/properties_generated.zig +++ b/src/css/properties/properties_generated.zig @@ -7436,699 +7436,931 @@ pub const PropertyId = union(PropertyIdTag) { } pub fn fromNameAndPrefix(name1: []const u8, pre: VendorPrefix) ?PropertyId { - // TODO: todo_stuff.match_ignore_ascii_case - if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "background-color")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"background-color"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "background-image")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"background-image"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "background-position-x")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"background-position-x"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "background-position-y")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"background-position-y"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "background-position")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"background-position"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "background-size")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"background-size"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "background-repeat")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"background-repeat"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "background-attachment")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"background-attachment"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "background-clip")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"background-clip" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "background-origin")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"background-origin"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "background")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .background; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "box-shadow")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"box-shadow" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "opacity")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .opacity; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "color")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .color; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "display")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .display; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "visibility")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .visibility; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "width")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .width; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "height")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .height; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "min-width")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"min-width"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "min-height")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"min-height"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "max-width")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"max-width"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "max-height")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"max-height"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "block-size")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"block-size"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "inline-size")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"inline-size"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "min-block-size")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"min-block-size"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "min-inline-size")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"min-inline-size"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "max-block-size")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"max-block-size"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "max-inline-size")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"max-inline-size"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "box-sizing")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"box-sizing" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "aspect-ratio")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"aspect-ratio"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "overflow")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .overflow; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "overflow-x")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"overflow-x"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "overflow-y")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"overflow-y"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "text-overflow")) { - const allowed_prefixes = VendorPrefix{ .none = true, .o = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"text-overflow" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "position")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .position; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "top")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .top; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "bottom")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .bottom; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "left")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .left; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "right")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .right; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "inset-block-start")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"inset-block-start"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "inset-block-end")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"inset-block-end"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "inset-inline-start")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"inset-inline-start"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "inset-inline-end")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"inset-inline-end"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "inset-block")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"inset-block"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "inset-inline")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"inset-inline"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "inset")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .inset; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-spacing")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-spacing"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-top-color")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-top-color"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-bottom-color")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-bottom-color"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-left-color")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-left-color"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-right-color")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-right-color"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-block-start-color")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-block-start-color"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-block-end-color")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-block-end-color"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-inline-start-color")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-inline-start-color"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-inline-end-color")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-inline-end-color"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-top-style")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-top-style"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-bottom-style")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-bottom-style"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-left-style")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-left-style"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-right-style")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-right-style"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-block-start-style")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-block-start-style"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-block-end-style")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-block-end-style"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-inline-start-style")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-inline-start-style"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-inline-end-style")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-inline-end-style"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-top-width")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-top-width"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-bottom-width")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-bottom-width"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-left-width")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-left-width"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-right-width")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-right-width"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-block-start-width")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-block-start-width"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-block-end-width")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-block-end-width"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-inline-start-width")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-inline-start-width"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-inline-end-width")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-inline-end-width"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-top-left-radius")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"border-top-left-radius" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-top-right-radius")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"border-top-right-radius" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-bottom-left-radius")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"border-bottom-left-radius" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-bottom-right-radius")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"border-bottom-right-radius" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-start-start-radius")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-start-start-radius"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-start-end-radius")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-start-end-radius"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-end-start-radius")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-end-start-radius"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-end-end-radius")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-end-end-radius"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-radius")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"border-radius" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-image-source")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-image-source"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-image-outset")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-image-outset"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-image-repeat")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-image-repeat"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-image-width")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-image-width"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-image-slice")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-image-slice"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-image")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true, .o = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"border-image" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-color")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-color"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-style")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-style"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-width")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-width"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-block-color")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-block-color"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-block-style")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-block-style"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-block-width")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-block-width"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-inline-color")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-inline-color"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-inline-style")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-inline-style"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-inline-width")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-inline-width"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .border; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-top")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-top"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-bottom")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-bottom"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-left")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-left"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-right")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-right"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-block")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-block"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-block-start")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-block-start"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-block-end")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-block-end"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-inline")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-inline"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-inline-start")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-inline-start"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "border-inline-end")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"border-inline-end"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "outline")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .outline; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "outline-color")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"outline-color"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "outline-style")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"outline-style"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "outline-width")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"outline-width"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-direction")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .ms = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"flex-direction" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-wrap")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .ms = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"flex-wrap" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-flow")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .ms = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"flex-flow" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-grow")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"flex-grow" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-shrink")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"flex-shrink" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-basis")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"flex-basis" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .ms = true }; - if (allowed_prefixes.contains(pre)) return .{ .flex = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "order")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .order = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "align-content")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"align-content" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "justify-content")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"justify-content" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "place-content")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"place-content"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "align-self")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"align-self" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "justify-self")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"justify-self"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "place-self")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"place-self"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "align-items")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"align-items" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "justify-items")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"justify-items"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "place-items")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"place-items"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "row-gap")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"row-gap"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "column-gap")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"column-gap"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "gap")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .gap; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "box-orient")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"box-orient" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "box-direction")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"box-direction" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "box-ordinal-group")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"box-ordinal-group" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "box-align")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"box-align" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "box-flex")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"box-flex" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "box-flex-group")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"box-flex-group" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "box-pack")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"box-pack" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "box-lines")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"box-lines" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-pack")) { - const allowed_prefixes = VendorPrefix{ .none = true, .ms = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"flex-pack" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-order")) { - const allowed_prefixes = VendorPrefix{ .none = true, .ms = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"flex-order" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-align")) { - const allowed_prefixes = VendorPrefix{ .none = true, .ms = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"flex-align" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-item-align")) { - const allowed_prefixes = VendorPrefix{ .none = true, .ms = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"flex-item-align" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-line-pack")) { - const allowed_prefixes = VendorPrefix{ .none = true, .ms = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"flex-line-pack" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-positive")) { - const allowed_prefixes = VendorPrefix{ .none = true, .ms = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"flex-positive" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-negative")) { - const allowed_prefixes = VendorPrefix{ .none = true, .ms = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"flex-negative" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "flex-preferred-size")) { - const allowed_prefixes = VendorPrefix{ .none = true, .ms = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"flex-preferred-size" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "margin-top")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"margin-top"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "margin-bottom")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"margin-bottom"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "margin-left")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"margin-left"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "margin-right")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"margin-right"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "margin-block-start")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"margin-block-start"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "margin-block-end")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"margin-block-end"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "margin-inline-start")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"margin-inline-start"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "margin-inline-end")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"margin-inline-end"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "margin-block")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"margin-block"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "margin-inline")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"margin-inline"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "margin")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .margin; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "padding-top")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"padding-top"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "padding-bottom")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"padding-bottom"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "padding-left")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"padding-left"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "padding-right")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"padding-right"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "padding-block-start")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"padding-block-start"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "padding-block-end")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"padding-block-end"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "padding-inline-start")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"padding-inline-start"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "padding-inline-end")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"padding-inline-end"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "padding-block")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"padding-block"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "padding-inline")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"padding-inline"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "padding")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .padding; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-margin-top")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"scroll-margin-top"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-margin-bottom")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"scroll-margin-bottom"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-margin-left")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"scroll-margin-left"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-margin-right")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"scroll-margin-right"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-margin-block-start")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"scroll-margin-block-start"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-margin-block-end")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"scroll-margin-block-end"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-margin-inline-start")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"scroll-margin-inline-start"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-margin-inline-end")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"scroll-margin-inline-end"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-margin-block")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"scroll-margin-block"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-margin-inline")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"scroll-margin-inline"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-margin")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"scroll-margin"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-padding-top")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"scroll-padding-top"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-padding-bottom")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"scroll-padding-bottom"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-padding-left")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"scroll-padding-left"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-padding-right")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"scroll-padding-right"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-padding-block-start")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"scroll-padding-block-start"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-padding-block-end")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"scroll-padding-block-end"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-padding-inline-start")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"scroll-padding-inline-start"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-padding-inline-end")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"scroll-padding-inline-end"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-padding-block")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"scroll-padding-block"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-padding-inline")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"scroll-padding-inline"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "scroll-padding")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"scroll-padding"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "font-weight")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"font-weight"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "font-size")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"font-size"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "font-stretch")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"font-stretch"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "font-family")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"font-family"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "font-style")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"font-style"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "font-variant-caps")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"font-variant-caps"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "line-height")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"line-height"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "font")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .font; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "text-decoration-color")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"text-decoration-color" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "text-emphasis-color")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"text-emphasis-color" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "text-shadow")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"text-shadow"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "direction")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .direction; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "composes")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .composes; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-image")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"mask-image" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-mode")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"mask-mode"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-repeat")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"mask-repeat" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-position-x")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"mask-position-x"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-position-y")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"mask-position-y"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-position")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"mask-position" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-clip")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"mask-clip" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-origin")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"mask-origin" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-size")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"mask-size" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-composite")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"mask-composite"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-type")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"mask-type"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .mask = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-border-source")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"mask-border-source"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-border-mode")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"mask-border-mode"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-border-slice")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"mask-border-slice"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-border-width")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"mask-border-width"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-border-outset")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"mask-border-outset"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-border-repeat")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"mask-border-repeat"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-border")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"mask-border"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "-webkit-mask-composite")) { - const allowed_prefixes = VendorPrefix{ .none = true }; - if (allowed_prefixes.contains(pre)) return .@"-webkit-mask-composite"; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-source-type")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"mask-source-type" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-box-image")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"mask-box-image" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-box-image-source")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"mask-box-image-source" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-box-image-slice")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"mask-box-image-slice" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-box-image-width")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"mask-box-image-width" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-box-image-outset")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"mask-box-image-outset" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "mask-box-image-repeat")) { - const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; - if (allowed_prefixes.contains(pre)) return .{ .@"mask-box-image-repeat" = pre }; - } else if (bun.strings.eqlCaseInsensitiveASCIIICheckLength(name1, "all")) {} else { - return null; + const Enum = enum { @"background-color", @"background-image", @"background-position-x", @"background-position-y", @"background-position", @"background-size", @"background-repeat", @"background-attachment", @"background-clip", @"background-origin", background, @"box-shadow", opacity, color, display, visibility, width, height, @"min-width", @"min-height", @"max-width", @"max-height", @"block-size", @"inline-size", @"min-block-size", @"min-inline-size", @"max-block-size", @"max-inline-size", @"box-sizing", @"aspect-ratio", overflow, @"overflow-x", @"overflow-y", @"text-overflow", position, top, bottom, left, right, @"inset-block-start", @"inset-block-end", @"inset-inline-start", @"inset-inline-end", @"inset-block", @"inset-inline", inset, @"border-spacing", @"border-top-color", @"border-bottom-color", @"border-left-color", @"border-right-color", @"border-block-start-color", @"border-block-end-color", @"border-inline-start-color", @"border-inline-end-color", @"border-top-style", @"border-bottom-style", @"border-left-style", @"border-right-style", @"border-block-start-style", @"border-block-end-style", @"border-inline-start-style", @"border-inline-end-style", @"border-top-width", @"border-bottom-width", @"border-left-width", @"border-right-width", @"border-block-start-width", @"border-block-end-width", @"border-inline-start-width", @"border-inline-end-width", @"border-top-left-radius", @"border-top-right-radius", @"border-bottom-left-radius", @"border-bottom-right-radius", @"border-start-start-radius", @"border-start-end-radius", @"border-end-start-radius", @"border-end-end-radius", @"border-radius", @"border-image-source", @"border-image-outset", @"border-image-repeat", @"border-image-width", @"border-image-slice", @"border-image", @"border-color", @"border-style", @"border-width", @"border-block-color", @"border-block-style", @"border-block-width", @"border-inline-color", @"border-inline-style", @"border-inline-width", border, @"border-top", @"border-bottom", @"border-left", @"border-right", @"border-block", @"border-block-start", @"border-block-end", @"border-inline", @"border-inline-start", @"border-inline-end", outline, @"outline-color", @"outline-style", @"outline-width", @"flex-direction", @"flex-wrap", @"flex-flow", @"flex-grow", @"flex-shrink", @"flex-basis", flex, order, @"align-content", @"justify-content", @"place-content", @"align-self", @"justify-self", @"place-self", @"align-items", @"justify-items", @"place-items", @"row-gap", @"column-gap", gap, @"box-orient", @"box-direction", @"box-ordinal-group", @"box-align", @"box-flex", @"box-flex-group", @"box-pack", @"box-lines", @"flex-pack", @"flex-order", @"flex-align", @"flex-item-align", @"flex-line-pack", @"flex-positive", @"flex-negative", @"flex-preferred-size", @"margin-top", @"margin-bottom", @"margin-left", @"margin-right", @"margin-block-start", @"margin-block-end", @"margin-inline-start", @"margin-inline-end", @"margin-block", @"margin-inline", margin, @"padding-top", @"padding-bottom", @"padding-left", @"padding-right", @"padding-block-start", @"padding-block-end", @"padding-inline-start", @"padding-inline-end", @"padding-block", @"padding-inline", padding, @"scroll-margin-top", @"scroll-margin-bottom", @"scroll-margin-left", @"scroll-margin-right", @"scroll-margin-block-start", @"scroll-margin-block-end", @"scroll-margin-inline-start", @"scroll-margin-inline-end", @"scroll-margin-block", @"scroll-margin-inline", @"scroll-margin", @"scroll-padding-top", @"scroll-padding-bottom", @"scroll-padding-left", @"scroll-padding-right", @"scroll-padding-block-start", @"scroll-padding-block-end", @"scroll-padding-inline-start", @"scroll-padding-inline-end", @"scroll-padding-block", @"scroll-padding-inline", @"scroll-padding", @"font-weight", @"font-size", @"font-stretch", @"font-family", @"font-style", @"font-variant-caps", @"line-height", font, @"text-decoration-color", @"text-emphasis-color", @"text-shadow", direction, composes, @"mask-image", @"mask-mode", @"mask-repeat", @"mask-position-x", @"mask-position-y", @"mask-position", @"mask-clip", @"mask-origin", @"mask-size", @"mask-composite", @"mask-type", mask, @"mask-border-source", @"mask-border-mode", @"mask-border-slice", @"mask-border-width", @"mask-border-outset", @"mask-border-repeat", @"mask-border", @"-webkit-mask-composite", @"mask-source-type", @"mask-box-image", @"mask-box-image-source", @"mask-box-image-slice", @"mask-box-image-width", @"mask-box-image-outset", @"mask-box-image-repeat" }; + const Map = comptime bun.ComptimeEnumMap(Enum); + if (Map.getASCIIICaseInsensitive(name1)) |prop| { + switch (prop) { + .@"background-color" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"background-color"; + }, + .@"background-image" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"background-image"; + }, + .@"background-position-x" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"background-position-x"; + }, + .@"background-position-y" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"background-position-y"; + }, + .@"background-position" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"background-position"; + }, + .@"background-size" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"background-size"; + }, + .@"background-repeat" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"background-repeat"; + }, + .@"background-attachment" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"background-attachment"; + }, + .@"background-clip" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"background-clip" = pre }; + }, + .@"background-origin" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"background-origin"; + }, + .background => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .background; + }, + .@"box-shadow" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"box-shadow" = pre }; + }, + .opacity => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .opacity; + }, + .color => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .color; + }, + .display => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .display; + }, + .visibility => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .visibility; + }, + .width => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .width; + }, + .height => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .height; + }, + .@"min-width" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"min-width"; + }, + .@"min-height" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"min-height"; + }, + .@"max-width" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"max-width"; + }, + .@"max-height" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"max-height"; + }, + .@"block-size" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"block-size"; + }, + .@"inline-size" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"inline-size"; + }, + .@"min-block-size" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"min-block-size"; + }, + .@"min-inline-size" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"min-inline-size"; + }, + .@"max-block-size" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"max-block-size"; + }, + .@"max-inline-size" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"max-inline-size"; + }, + .@"box-sizing" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"box-sizing" = pre }; + }, + .@"aspect-ratio" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"aspect-ratio"; + }, + .overflow => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .overflow; + }, + .@"overflow-x" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"overflow-x"; + }, + .@"overflow-y" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"overflow-y"; + }, + .@"text-overflow" => { + const allowed_prefixes = VendorPrefix{ .none = true, .o = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"text-overflow" = pre }; + }, + .position => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .position; + }, + .top => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .top; + }, + .bottom => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .bottom; + }, + .left => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .left; + }, + .right => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .right; + }, + .@"inset-block-start" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"inset-block-start"; + }, + .@"inset-block-end" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"inset-block-end"; + }, + .@"inset-inline-start" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"inset-inline-start"; + }, + .@"inset-inline-end" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"inset-inline-end"; + }, + .@"inset-block" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"inset-block"; + }, + .@"inset-inline" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"inset-inline"; + }, + .inset => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .inset; + }, + .@"border-spacing" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-spacing"; + }, + .@"border-top-color" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-top-color"; + }, + .@"border-bottom-color" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-bottom-color"; + }, + .@"border-left-color" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-left-color"; + }, + .@"border-right-color" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-right-color"; + }, + .@"border-block-start-color" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-block-start-color"; + }, + .@"border-block-end-color" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-block-end-color"; + }, + .@"border-inline-start-color" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-inline-start-color"; + }, + .@"border-inline-end-color" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-inline-end-color"; + }, + .@"border-top-style" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-top-style"; + }, + .@"border-bottom-style" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-bottom-style"; + }, + .@"border-left-style" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-left-style"; + }, + .@"border-right-style" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-right-style"; + }, + .@"border-block-start-style" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-block-start-style"; + }, + .@"border-block-end-style" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-block-end-style"; + }, + .@"border-inline-start-style" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-inline-start-style"; + }, + .@"border-inline-end-style" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-inline-end-style"; + }, + .@"border-top-width" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-top-width"; + }, + .@"border-bottom-width" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-bottom-width"; + }, + .@"border-left-width" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-left-width"; + }, + .@"border-right-width" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-right-width"; + }, + .@"border-block-start-width" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-block-start-width"; + }, + .@"border-block-end-width" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-block-end-width"; + }, + .@"border-inline-start-width" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-inline-start-width"; + }, + .@"border-inline-end-width" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-inline-end-width"; + }, + .@"border-top-left-radius" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"border-top-left-radius" = pre }; + }, + .@"border-top-right-radius" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"border-top-right-radius" = pre }; + }, + .@"border-bottom-left-radius" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"border-bottom-left-radius" = pre }; + }, + .@"border-bottom-right-radius" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"border-bottom-right-radius" = pre }; + }, + .@"border-start-start-radius" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-start-start-radius"; + }, + .@"border-start-end-radius" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-start-end-radius"; + }, + .@"border-end-start-radius" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-end-start-radius"; + }, + .@"border-end-end-radius" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-end-end-radius"; + }, + .@"border-radius" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"border-radius" = pre }; + }, + .@"border-image-source" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-image-source"; + }, + .@"border-image-outset" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-image-outset"; + }, + .@"border-image-repeat" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-image-repeat"; + }, + .@"border-image-width" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-image-width"; + }, + .@"border-image-slice" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-image-slice"; + }, + .@"border-image" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true, .o = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"border-image" = pre }; + }, + .@"border-color" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-color"; + }, + .@"border-style" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-style"; + }, + .@"border-width" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-width"; + }, + .@"border-block-color" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-block-color"; + }, + .@"border-block-style" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-block-style"; + }, + .@"border-block-width" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-block-width"; + }, + .@"border-inline-color" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-inline-color"; + }, + .@"border-inline-style" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-inline-style"; + }, + .@"border-inline-width" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-inline-width"; + }, + .border => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .border; + }, + .@"border-top" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-top"; + }, + .@"border-bottom" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-bottom"; + }, + .@"border-left" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-left"; + }, + .@"border-right" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-right"; + }, + .@"border-block" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-block"; + }, + .@"border-block-start" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-block-start"; + }, + .@"border-block-end" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-block-end"; + }, + .@"border-inline" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-inline"; + }, + .@"border-inline-start" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-inline-start"; + }, + .@"border-inline-end" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"border-inline-end"; + }, + .outline => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .outline; + }, + .@"outline-color" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"outline-color"; + }, + .@"outline-style" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"outline-style"; + }, + .@"outline-width" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"outline-width"; + }, + .@"flex-direction" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-direction" = pre }; + }, + .@"flex-wrap" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-wrap" = pre }; + }, + .@"flex-flow" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-flow" = pre }; + }, + .@"flex-grow" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-grow" = pre }; + }, + .@"flex-shrink" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-shrink" = pre }; + }, + .@"flex-basis" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-basis" = pre }; + }, + .flex => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .flex = pre }; + }, + .order => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .order = pre }; + }, + .@"align-content" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"align-content" = pre }; + }, + .@"justify-content" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"justify-content" = pre }; + }, + .@"place-content" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"place-content"; + }, + .@"align-self" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"align-self" = pre }; + }, + .@"justify-self" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"justify-self"; + }, + .@"place-self" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"place-self"; + }, + .@"align-items" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"align-items" = pre }; + }, + .@"justify-items" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"justify-items"; + }, + .@"place-items" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"place-items"; + }, + .@"row-gap" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"row-gap"; + }, + .@"column-gap" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"column-gap"; + }, + .gap => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .gap; + }, + .@"box-orient" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"box-orient" = pre }; + }, + .@"box-direction" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"box-direction" = pre }; + }, + .@"box-ordinal-group" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"box-ordinal-group" = pre }; + }, + .@"box-align" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"box-align" = pre }; + }, + .@"box-flex" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"box-flex" = pre }; + }, + .@"box-flex-group" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"box-flex-group" = pre }; + }, + .@"box-pack" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"box-pack" = pre }; + }, + .@"box-lines" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"box-lines" = pre }; + }, + .@"flex-pack" => { + const allowed_prefixes = VendorPrefix{ .none = true, .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-pack" = pre }; + }, + .@"flex-order" => { + const allowed_prefixes = VendorPrefix{ .none = true, .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-order" = pre }; + }, + .@"flex-align" => { + const allowed_prefixes = VendorPrefix{ .none = true, .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-align" = pre }; + }, + .@"flex-item-align" => { + const allowed_prefixes = VendorPrefix{ .none = true, .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-item-align" = pre }; + }, + .@"flex-line-pack" => { + const allowed_prefixes = VendorPrefix{ .none = true, .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-line-pack" = pre }; + }, + .@"flex-positive" => { + const allowed_prefixes = VendorPrefix{ .none = true, .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-positive" = pre }; + }, + .@"flex-negative" => { + const allowed_prefixes = VendorPrefix{ .none = true, .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-negative" = pre }; + }, + .@"flex-preferred-size" => { + const allowed_prefixes = VendorPrefix{ .none = true, .ms = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"flex-preferred-size" = pre }; + }, + .@"margin-top" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"margin-top"; + }, + .@"margin-bottom" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"margin-bottom"; + }, + .@"margin-left" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"margin-left"; + }, + .@"margin-right" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"margin-right"; + }, + .@"margin-block-start" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"margin-block-start"; + }, + .@"margin-block-end" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"margin-block-end"; + }, + .@"margin-inline-start" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"margin-inline-start"; + }, + .@"margin-inline-end" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"margin-inline-end"; + }, + .@"margin-block" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"margin-block"; + }, + .@"margin-inline" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"margin-inline"; + }, + .margin => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .margin; + }, + .@"padding-top" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"padding-top"; + }, + .@"padding-bottom" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"padding-bottom"; + }, + .@"padding-left" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"padding-left"; + }, + .@"padding-right" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"padding-right"; + }, + .@"padding-block-start" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"padding-block-start"; + }, + .@"padding-block-end" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"padding-block-end"; + }, + .@"padding-inline-start" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"padding-inline-start"; + }, + .@"padding-inline-end" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"padding-inline-end"; + }, + .@"padding-block" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"padding-block"; + }, + .@"padding-inline" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"padding-inline"; + }, + .padding => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .padding; + }, + .@"scroll-margin-top" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-margin-top"; + }, + .@"scroll-margin-bottom" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-margin-bottom"; + }, + .@"scroll-margin-left" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-margin-left"; + }, + .@"scroll-margin-right" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-margin-right"; + }, + .@"scroll-margin-block-start" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-margin-block-start"; + }, + .@"scroll-margin-block-end" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-margin-block-end"; + }, + .@"scroll-margin-inline-start" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-margin-inline-start"; + }, + .@"scroll-margin-inline-end" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-margin-inline-end"; + }, + .@"scroll-margin-block" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-margin-block"; + }, + .@"scroll-margin-inline" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-margin-inline"; + }, + .@"scroll-margin" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-margin"; + }, + .@"scroll-padding-top" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-padding-top"; + }, + .@"scroll-padding-bottom" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-padding-bottom"; + }, + .@"scroll-padding-left" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-padding-left"; + }, + .@"scroll-padding-right" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-padding-right"; + }, + .@"scroll-padding-block-start" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-padding-block-start"; + }, + .@"scroll-padding-block-end" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-padding-block-end"; + }, + .@"scroll-padding-inline-start" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-padding-inline-start"; + }, + .@"scroll-padding-inline-end" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-padding-inline-end"; + }, + .@"scroll-padding-block" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-padding-block"; + }, + .@"scroll-padding-inline" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-padding-inline"; + }, + .@"scroll-padding" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"scroll-padding"; + }, + .@"font-weight" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"font-weight"; + }, + .@"font-size" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"font-size"; + }, + .@"font-stretch" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"font-stretch"; + }, + .@"font-family" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"font-family"; + }, + .@"font-style" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"font-style"; + }, + .@"font-variant-caps" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"font-variant-caps"; + }, + .@"line-height" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"line-height"; + }, + .font => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .font; + }, + .@"text-decoration-color" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true, .moz = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"text-decoration-color" = pre }; + }, + .@"text-emphasis-color" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"text-emphasis-color" = pre }; + }, + .@"text-shadow" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"text-shadow"; + }, + .direction => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .direction; + }, + .composes => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .composes; + }, + .@"mask-image" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-image" = pre }; + }, + .@"mask-mode" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-mode"; + }, + .@"mask-repeat" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-repeat" = pre }; + }, + .@"mask-position-x" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-position-x"; + }, + .@"mask-position-y" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-position-y"; + }, + .@"mask-position" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-position" = pre }; + }, + .@"mask-clip" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-clip" = pre }; + }, + .@"mask-origin" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-origin" = pre }; + }, + .@"mask-size" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-size" = pre }; + }, + .@"mask-composite" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-composite"; + }, + .@"mask-type" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-type"; + }, + .mask => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .mask = pre }; + }, + .@"mask-border-source" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-border-source"; + }, + .@"mask-border-mode" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-border-mode"; + }, + .@"mask-border-slice" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-border-slice"; + }, + .@"mask-border-width" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-border-width"; + }, + .@"mask-border-outset" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-border-outset"; + }, + .@"mask-border-repeat" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-border-repeat"; + }, + .@"mask-border" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"mask-border"; + }, + .@"-webkit-mask-composite" => { + const allowed_prefixes = VendorPrefix{ .none = true }; + if (allowed_prefixes.contains(pre)) return .@"-webkit-mask-composite"; + }, + .@"mask-source-type" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-source-type" = pre }; + }, + .@"mask-box-image" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-box-image" = pre }; + }, + .@"mask-box-image-source" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-box-image-source" = pre }; + }, + .@"mask-box-image-slice" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-box-image-slice" = pre }; + }, + .@"mask-box-image-width" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-box-image-width" = pre }; + }, + .@"mask-box-image-outset" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-box-image-outset" = pre }; + }, + .@"mask-box-image-repeat" => { + const allowed_prefixes = VendorPrefix{ .none = true, .webkit = true }; + if (allowed_prefixes.contains(pre)) return .{ .@"mask-box-image-repeat" = pre }; + }, + } } return null; diff --git a/src/css/rules/rules.zig b/src/css/rules/rules.zig index 8c1fb5b4baeecc..a66ebd6cd254ca 100644 --- a/src/css/rules/rules.zig +++ b/src/css/rules/rules.zig @@ -37,6 +37,8 @@ pub const scope = @import("./scope.zig"); pub const media = @import("./media.zig"); pub const starting_style = @import("./starting_style.zig"); +pub const tailwind = @import("./tailwind.zig"); + const debug = bun.Output.scoped(.CSS_MINIFY, false); pub fn CssRule(comptime Rule: type) type { diff --git a/src/css/rules/tailwind.zig b/src/css/rules/tailwind.zig new file mode 100644 index 00000000000000..b3e15e3e1bf0e7 --- /dev/null +++ b/src/css/rules/tailwind.zig @@ -0,0 +1,60 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const bun = @import("root").bun; +const logger = bun.logger; +const Log = logger.Log; + +pub const css = @import("../css_parser.zig"); +pub const css_values = @import("../values/values.zig"); +pub const Error = css.Error; +const Printer = css.Printer; +const PrintErr = css.PrintErr; + +/// @tailwind +/// https://github.com/tailwindlabs/tailwindcss.com/blob/4d6ac11425d96bc963f936e0157df460a364c43b/src/pages/docs/functions-and-directives.mdx?plain=1#L13 +pub const TailwindAtRule = struct { + style_name: TailwindStyleName, + /// The location of the rule in the source file. + loc: css.Location, + + pub fn toCss(this: *const @This(), comptime W: type, dest: *Printer(W)) PrintErr!void { + try dest.writeStr("@tailwind"); + try dest.whitespace(); + try this.style_name.toCss(W, dest); + try dest.writeChar(';'); + } + + pub fn deepClone(this: *const @This(), _: std.mem.Allocator) @This() { + return this.*; + } +}; + +pub const TailwindStyleName = enum { + /// This injects Tailwind's base styles and any base styles registered by + /// plugins. + base, + /// This injects Tailwind's component classes and any component classes + /// registered by plugins. + components, + /// This injects Tailwind's utility classes and any utility classes registered + /// by plugins. + utilities, + /// Use this directive to control where Tailwind injects the hover, focus, + /// responsive, dark mode, and other variants of each class. + /// + /// If omitted, Tailwind will append these classes to the very end of + /// your stylesheet by default. + variants, + + pub fn asStr(this: *const @This()) []const u8 { + return css.enum_property_util.asStr(@This(), this); + } + + pub fn parse(input: *css.Parser) css.Result(@This()) { + return css.enum_property_util.parse(@This(), input); + } + + pub fn toCss(this: *const @This(), comptime W: type, dest: *Printer(W)) PrintErr!void { + return css.enum_property_util.toCss(@This(), this, W, dest); + } +}; diff --git a/src/import_record.zig b/src/import_record.zig index 7d27ae820e456b..e16c6b94dfbb1b 100644 --- a/src/import_record.zig +++ b/src/import_record.zig @@ -193,6 +193,7 @@ pub const ImportRecord = struct { with_type_file, css, + tailwind, pub fn loader(this: Tag) ?bun.options.Loader { return switch (this) { diff --git a/src/js/builtins/BundlerPlugin.ts b/src/js/builtins/BundlerPlugin.ts index db78902f2d69cf..484308874bc48e 100644 --- a/src/js/builtins/BundlerPlugin.ts +++ b/src/js/builtins/BundlerPlugin.ts @@ -23,6 +23,8 @@ interface BundlerPlugin { onResolveAsync(internalID, a, b, c): void; addError(internalID, error, number): void; addFilter(filter, namespace, number): void; + generateDeferPromise(): Promise; + promises: Array> | undefined; } // Extra types @@ -47,7 +49,14 @@ interface PluginBuilderExt extends PluginBuilder { esbuild: any; } -export function runSetupFunction(this: BundlerPlugin, setup: Setup, config: BuildConfigExt) { +export function runSetupFunction( + this: BundlerPlugin, + setup: Setup, + config: BuildConfigExt, + promises: Array> | undefined, + is_last: boolean, +) { + this.promises = promises; var onLoadPlugins = new Map(); var onResolvePlugins = new Map(); @@ -99,6 +108,21 @@ export function runSetupFunction(this: BundlerPlugin, setup: Setup, config: Buil validate(filterObject, callback, onResolvePlugins); } + const self = this; + function onStart(callback) { + if (!$isCallable(callback)) { + throw new TypeError("callback must be a function"); + } + + const ret = callback(); + if ($isPromise(ret)) { + if (($getPromiseInternalField(ret, $promiseFieldFlags) & $promiseStateMask) != $promiseStateFulfilled) { + self.promises ??= []; + self.promises.push(ret); + } + } + } + const processSetupResult = () => { var anyOnLoad = false, anyOnResolve = false; @@ -151,7 +175,11 @@ export function runSetupFunction(this: BundlerPlugin, setup: Setup, config: Buil } } - return anyOnLoad || anyOnResolve; + if (is_last) { + this.promises = undefined; + } + + return this.promises; }; var setupResult = setup({ @@ -160,7 +188,7 @@ export function runSetupFunction(this: BundlerPlugin, setup: Setup, config: Buil onEnd: notImplementedIssueFn(2771, "On-end callbacks"), onLoad, onResolve, - onStart: notImplementedIssueFn(2771, "On-start callbacks"), + onStart, resolve: notImplementedIssueFn(2771, "build.resolve()"), module: () => { throw new TypeError("module() is not supported in Bun.build() yet. Only via Bun.plugin() at runtime"); @@ -184,10 +212,21 @@ export function runSetupFunction(this: BundlerPlugin, setup: Setup, config: Buil if ($getPromiseInternalField(setupResult, $promiseFieldFlags) & $promiseStateFulfilled) { setupResult = $getPromiseInternalField(setupResult, $promiseFieldReactionsOrResult); } else { - return setupResult.$then(processSetupResult); + return setupResult.$then(() => { + if (is_last && self.promises !== undefined && self.promises.length > 0) { + const awaitAll = Promise.all(self.promises); + return awaitAll.$then(processSetupResult); + } + return processSetupResult(); + }); } } + if (is_last && this.promises !== undefined && this.promises.length > 0) { + const awaitAll = Promise.all(this.promises); + return awaitAll.$then(processSetupResult); + } + return processSetupResult(); } @@ -299,7 +338,8 @@ export function runOnLoadPlugins(this: BundlerPlugin, internalID, path, namespac const LOADERS_MAP = $LoaderLabelToId; const loaderName = $LoaderIdToLabel[defaultLoaderId]; - var promiseResult = (async (internalID, path, namespace, defaultLoader) => { + const generateDefer = () => this.generateDeferPromise(internalID); + var promiseResult = (async (internalID, path, namespace, defaultLoader, generateDefer) => { var results = this.onLoad.$get(namespace); if (!results) { this.onLoadAsync(internalID, null, null); @@ -314,6 +354,7 @@ export function runOnLoadPlugins(this: BundlerPlugin, internalID, path, namespac // suffix // pluginData loader: defaultLoader, + defer: generateDefer, }); while ( @@ -353,7 +394,7 @@ export function runOnLoadPlugins(this: BundlerPlugin, internalID, path, namespac this.onLoadAsync(internalID, null, null); return null; - })(internalID, path, namespace, loaderName); + })(internalID, path, namespace, loaderName, generateDefer); while ( promiseResult && diff --git a/test/bundler/expectBundled.ts b/test/bundler/expectBundled.ts index 9a49939e09f76a..50607cf44f0599 100644 --- a/test/bundler/expectBundled.ts +++ b/test/bundler/expectBundled.ts @@ -380,7 +380,7 @@ export interface BundlerTestRef { options: BundlerTestInput; } -interface ErrorMeta { +export interface ErrorMeta { file: string; error: string; line?: string; @@ -718,6 +718,7 @@ function expectBundled( minifySyntax && `--minify-syntax`, minifyWhitespace && `--minify-whitespace`, globalName && `--global-name=${globalName}`, + experimentalCss && "--experimental-css", external && external.map(x => `--external:${x}`), packages && ["--packages", packages], conditions && `--conditions=${conditions.join(",")}`, @@ -1016,6 +1017,7 @@ function expectBundled( publicPath, emitDCEAnnotations, ignoreDCEAnnotations, + experimentalCss, drop, } as BuildConfig; @@ -1131,6 +1133,7 @@ for (const [key, blob] of build.outputs) { return testRef(id, opts); } + throw new Error("Bundle Failed\n" + [...allErrors].map(formatError).join("\n")); } else if (expectedErrors && expectedErrors.length > 0) { throw new Error("Errors were expected while bundling:\n" + expectedErrors.map(formatError).join("\n")); diff --git a/test/js/bun/plugin/plugins.test.ts b/test/js/bun/plugin/plugins.test.ts index 1b2013c534002d..955a719f0e5e40 100644 --- a/test/js/bun/plugin/plugins.test.ts +++ b/test/js/bun/plugin/plugins.test.ts @@ -1,7 +1,7 @@ /// import { plugin } from "bun"; -import { describe, expect, it } from "bun:test"; -import { resolve } from "path"; +import { describe, expect, it, test } from "bun:test"; +import path, { dirname, join, resolve } from "path"; declare global { var failingObject: any; @@ -187,6 +187,9 @@ plugin({ // This is to test that it works when imported from a separate file import "../../third_party/svelte"; import "./module-plugins"; +import { itBundled } from "bundler/expectBundled"; +import { bunEnv, bunExe, tempDirWithFiles } from "harness"; +import { filter } from "js/node/test/fixtures/aead-vectors"; describe("require", () => { it("SSRs `

Hello world!

` with Svelte", () => { @@ -480,3 +483,631 @@ describe("errors", () => { expect(text).toBe(result); }); }); + +describe("start", () => { + { + let state: string = "Should not see this!"; + + itBundled("works", { + experimentalCss: true, + minifyWhitespace: true, + files: { + "/entry.css": /* css */ ` + body { + background: white; + color: blue; } + `, + }, + plugins: [ + { + name: "demo", + setup(build) { + build.onStart(() => { + state = "red"; + }); + + build.onLoad({ filter: /\.css/ }, async ({ path }) => { + console.log("[plugin] Path", path); + return { + contents: `body { color: ${state} }`, + loader: "css", + }; + }); + }, + }, + ], + outfile: "/out.js", + onAfterBundle(api) { + api.expectFile("/out.js").toEqualIgnoringWhitespace(`body{color:${state}}`); + }, + }); + } + + { + type Action = "onLoad" | "onStart"; + let actions: Action[] = []; + + itBundled("executes before everything", { + experimentalCss: true, + minifyWhitespace: true, + files: { + "/entry.css": /* css */ ` + body { + background: white; + color: blue; } + `, + }, + plugins: [ + { + name: "demo", + setup(build) { + build.onLoad({ filter: /\.css/ }, async ({ path }) => { + actions.push("onLoad"); + return { + contents: `body { color: red }`, + loader: "css", + }; + }); + + build.onStart(() => { + actions.push("onStart"); + }); + }, + }, + ], + outfile: "/out.js", + onAfterBundle(api) { + api.expectFile("/out.js").toEqualIgnoringWhitespace(`body{ color: red }`); + + expect(actions).toStrictEqual(["onStart", "onLoad"]); + }, + }); + } + + { + let action: string[] = []; + itBundled("executes after all plugins have been setup", { + experimentalCss: true, + minifyWhitespace: true, + files: { + "/entry.css": /* css */ ` + body { + background: white; + color: blue; } + `, + }, + plugins: [ + { + name: "onStart 1", + setup(build) { + build.onStart(async () => { + action.push("onStart 1 setup"); + await Bun.sleep(1000); + action.push("onStart 1 complete"); + }); + }, + }, + { + name: "onStart 2", + setup(build) { + build.onStart(async () => { + action.push("onStart 2 setup"); + await Bun.sleep(1000); + action.push("onStart 2 complete"); + }); + }, + }, + { + name: "onStart 3", + setup(build) { + build.onStart(async () => { + action.push("onStart 3 setup"); + await Bun.sleep(1000); + action.push("onStart 3 complete"); + }); + }, + }, + ], + outfile: "/out.js", + onAfterBundle(api) { + expect(action.slice(0, 3)).toStrictEqual(["onStart 1 setup", "onStart 2 setup", "onStart 3 setup"]); + expect(new Set(action.slice(3))).toStrictEqual( + new Set(["onStart 1 complete", "onStart 2 complete", "onStart 3 complete"]), + ); + }, + }); + } + + { + let action: string[] = []; + test("LMAO", async () => { + const folder = tempDirWithFiles("plz", { + "index.ts": "export const foo = {}", + }); + try { + const result = await Bun.build({ + entrypoints: [path.join(folder, "index.ts")], + experimentalCss: true, + minify: true, + plugins: [ + { + name: "onStart 1", + setup(build) { + build.onStart(async () => { + action.push("onStart 1 setup"); + throw new Error("WOOPS"); + // await Bun.sleep(1000); + }); + }, + }, + { + name: "onStart 2", + setup(build) { + build.onStart(async () => { + action.push("onStart 2 setup"); + await Bun.sleep(1000); + action.push("onStart 2 complete"); + }); + }, + }, + { + name: "onStart 3", + setup(build) { + build.onStart(async () => { + action.push("onStart 3 setup"); + await Bun.sleep(1000); + action.push("onStart 3 complete"); + }); + }, + }, + ], + }); + console.log(result); + } catch (err) { + expect(err).toBeDefined(); + return; + } + throw new Error("DIDNT GET ERRROR!"); + }); + } +}); + +describe("defer", () => { + { + type Action = { + type: "load" | "defer"; + path: string; + }; + let actions: Action[] = []; + function logLoad(path: string) { + actions.push({ type: "load", path: path.replaceAll("\\", "/") }); + } + function logDefer(path: string) { + actions.push({ type: "defer", path: path.replaceAll("\\", "/") }); + } + + itBundled("basic", { + experimentalCss: true, + files: { + "/index.ts": /* ts */ ` +import { lmao } from "./lmao.ts"; +import foo from "./a.css"; + +console.log("Foo", foo, lmao); + `, + "/lmao.ts": ` +import { foo } from "./foo.ts"; +export const lmao = "lolss"; +console.log(foo); + `, + "/foo.ts": ` + export const foo = 'lkdfjlsdf'; + console.log('hi')`, + "/a.css": ` + h1 { + color: blue; + } + `, + }, + entryPoints: ["index.ts"], + plugins: [ + { + name: "demo", + setup(build) { + build.onLoad({ filter: /\.(ts)/ }, async ({ defer, path }) => { + // console.log("Running on load plugin", path); + if (path.includes("index.ts")) { + logLoad(path); + return undefined; + } + logDefer(path); + await defer(); + logLoad(path); + return undefined; + }); + }, + }, + ], + outdir: "/out", + onAfterBundle(api) { + const expected_actions: Action[] = [ + { + type: "load", + path: "index.ts", + }, + { + type: "defer", + path: "lmao.ts", + }, + { + type: "load", + path: "lmao.ts", + }, + { + type: "defer", + path: "foo.ts", + }, + { + type: "load", + path: "foo.ts", + }, + ]; + + expect(actions.length).toBe(expected_actions.length); + for (let i = 0; i < expected_actions.length; i++) { + const expected = expected_actions[i]; + const action = actions[i]; + const filename = action.path.split("/").pop(); + + expect(action.type).toEqual(expected.type); + expect(filename).toEqual(expected.path); + } + }, + }); + } + + itBundled("edgecase", { + experimentalCss: true, + minifyWhitespace: true, + files: { + "/entry.css": /* css */ ` + body { + background: white; + color: black } + `, + }, + plugins: [ + { + name: "demo", + setup(build) { + build.onLoad({ filter: /\.css/ }, async ({ path }) => { + console.log("[plugin] Path", path); + return { + contents: 'h1 [this_worked="nice!"] { color: red; }', + loader: "css", + }; + }); + }, + }, + ], + outfile: "/out.js", + onAfterBundle(api) { + api.expectFile("/out.js").toContain(`h1 [this_worked=nice\\!]{color:red} +`); + }, + }); + + // encountered double free when CSS build has error + itBundled("shouldn't crash on CSS parse error", { + experimentalCss: true, + files: { + "/index.ts": /* ts */ ` + import { lmao } from "./lmao.ts"; + import foo from "./a.css"; + + console.log("Foo", foo, lmao); + `, + "/lmao.ts": ` + import { foo } from "./foo.ts"; + export const lmao = "lolss"; + console.log(foo); + `, + "/foo.ts": ` + export const foo = "LOL bro"; + console.log("FOOOO", foo); + `, + "/a.css": ` + /* helllooo friends */ + `, + }, + entryPoints: ["index.ts"], + plugins: [ + { + name: "demo", + setup(build) { + build.onLoad({ filter: /\.css/ }, async ({ path }) => { + console.log("[plugin] CSS path", path); + return { + // this fails, because it causes a Build error I think? + contents: `hello friends`, + loader: "css", + }; + }); + + build.onLoad({ filter: /\.(ts)/ }, async ({ defer, path }) => { + // console.log("Running on load plugin", path); + if (path.includes("index.ts")) { + console.log("[plugin] Path", path); + return undefined; + } + await defer(); + return undefined; + }); + }, + }, + ], + outdir: "/out", + bundleErrors: { + "/a.css": ["end_of_input"], + }, + }); + + itBundled("works as expected when onLoad error occurs after defer", { + experimentalCss: true, + files: { + "/index.ts": /* ts */ ` + import { lmao } from "./lmao.ts"; + import foo from "./a.css"; + + console.log("Foo", foo, lmao); + `, + "/lmao.ts": ` + import { foo } from "./foo.ts"; + export const lmao = "lolss"; + console.log(foo); + `, + "/foo.ts": ` + export const foo = "LOL bro"; + console.log("FOOOO", foo); + `, + "/a.css": ` + /* helllooo friends */ + `, + }, + entryPoints: ["index.ts"], + plugins: [ + { + name: "demo", + setup(build) { + build.onLoad({ filter: /\.css/ }, async ({ path }) => { + return { + // this fails, because it causes a Build error I think? + contents: `hello friends`, + loader: "css", + }; + }); + + build.onLoad({ filter: /\.(ts)/ }, async ({ defer, path }) => { + if (path.includes("index.ts")) { + return undefined; + } + await defer(); + throw new Error("woopsie"); + }); + }, + }, + ], + outdir: "/out", + bundleErrors: { + "/a.css": ["end_of_input"], + "/lmao.ts": ["woopsie"], + }, + }); + + itBundled("calling defer more than once errors", { + experimentalCss: true, + files: { + "/index.ts": /* ts */ ` + import { lmao } from "./lmao.ts"; + import foo from "./a.css"; + + console.log("Foo", foo, lmao); + `, + "/lmao.ts": ` + import { foo } from "./foo.ts"; + export const lmao = "lolss"; + console.log(foo); + `, + "/foo.ts": ` + export const foo = "LOL bro"; + console.log("FOOOO", foo); + `, + "/a.css": ` + /* helllooo friends */ + `, + }, + entryPoints: ["index.ts"], + plugins: [ + { + name: "demo", + setup(build) { + build.onLoad({ filter: /\.css/ }, async ({ path }) => { + return { + // this fails, because it causes a Build error I think? + contents: `hello friends`, + loader: "css", + }; + }); + + build.onLoad({ filter: /\.(ts)/ }, async ({ defer, path }) => { + if (path.includes("index.ts")) { + return undefined; + } + await defer(); + await defer(); + }); + }, + }, + ], + outdir: "/out", + bundleErrors: { + "/a.css": ["end_of_input"], + "/lmao.ts": ["can't call .defer() more than once within an onLoad plugin"], + }, + }); + + test("integration", async () => { + const folder = tempDirWithFiles("integration", { + "module_data.json": "{}", + "package.json": `{ + "name": "integration-test", + "version": "1.0.0", + "private": true, + "type": "module", + "dependencies": { + } + }`, + "src/index.ts": ` +import { greet } from "./utils/greetings"; +import { formatDate } from "./utils/dates"; +import { calculateTotal } from "./math/calculations"; +import { logger } from "./services/logger"; +import moduleData from "../module_data.json"; +import path from "path"; + + +await Bun.write(path.join(import.meta.dirname, 'output.json'), JSON.stringify(moduleData)) + +function main() { + const today = new Date(); + logger.info("Application started"); + + const total = calculateTotal([10, 20, 30, 40]); + console.log(greet("World")); + console.log(\`Today is \${formatDate(today)}\`); + console.log(\`Total: \${total}\`); +} +`, + "src/utils/greetings.ts": ` +export function greet(name: string): string { + return \`Hello \${name}!\`; +} +`, + "src/utils/dates.ts": ` +export function formatDate(date: Date): string { + return date.toLocaleDateString("en-US", { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric" + }); +} +`, + "src/math/calculations.ts": ` +export function calculateTotal(numbers: number[]): number { + return numbers.reduce((sum, num) => sum + num, 0); +} + +export function multiply(a: number, b: number): number { + return a * b; +} +`, + "src/services/logger.ts": ` +export const logger = { + info: (msg: string) => console.log(\`[INFO] \${msg}\`), + error: (msg: string) => console.error(\`[ERROR] \${msg}\`), + warn: (msg: string) => console.warn(\`[WARN] \${msg}\`) +}; +`, + }); + + const entrypoint = path.join(folder, "src", "index.ts"); + await Bun.$`${bunExe()} install`.env(bunEnv).cwd(folder); + + const outdir = path.join(folder, "dist"); + + const result = await Bun.build({ + entrypoints: [entrypoint], + outdir, + plugins: [ + { + name: "xXx123_import_checker_321xXx", + setup(build) { + type Import = { + imported: string[]; + dep: string; + }; + type Export = { + ident: string; + }; + let imports_and_exports: Record; exports: Array }> = {}; + + build.onLoad({ filter: /\.ts/ }, async ({ path }) => { + const contents = await Bun.$`cat ${path}`.quiet().text(); + + const import_regex = /import\s+(?:([\s\S]*?)\s+from\s+)?['"]([^'"]+)['"];/g; + const imports: Array = [...contents.toString().matchAll(import_regex)].map(m => ({ + imported: m + .slice(1, m.length - 1) + .map(match => (match[0] === "{" ? match.slice(2, match.length - 2) : match)), + dep: m[m.length - 1], + })); + + const export_regex = + /export\s+(?:default\s+|const\s+|let\s+|var\s+|function\s+|class\s+|enum\s+|type\s+|interface\s+)?([\w$]+)?(?:\s*=\s*|(?:\s*{[^}]*})?)?[^;]*;/g; + const exports: Array = [...contents.matchAll(export_regex)].map(m => ({ + ident: m[1], + })); + + imports_and_exports[path.replaceAll("\\", "/").split("/").pop()!] = { imports, exports }; + return undefined; + }); + + build.onLoad({ filter: /module_data\.json/ }, async ({ defer }) => { + await defer(); + const contents = JSON.stringify(imports_and_exports); + + return { + contents, + loader: "json", + }; + }); + }, + }, + ], + }); + + expect(result.success).toBeTrue(); + await Bun.$`${bunExe()} run ${result.outputs[0].path}`; + const output = await Bun.$`cat ${path.join(folder, "dist", "output.json")}`.json(); + expect(output).toStrictEqual({ + "index.ts": { + "imports": [ + { "imported": ["greet"], "dep": "./utils/greetings" }, + { "imported": ["formatDate"], "dep": "./utils/dates" }, + { "imported": ["calculateTotal"], "dep": "./math/calculations" }, + { "imported": ["logger"], "dep": "./services/logger" }, + { "imported": ["moduleData"], "dep": "../module_data.json" }, + { "imported": ["path"], "dep": "path" }, + ], + "exports": [], + }, + "greetings.ts": { + "imports": [], + "exports": [{ "ident": "greet" }], + }, + "dates.ts": { + "imports": [], + "exports": [{ "ident": "formatDate" }], + }, + "calculations.ts": { + "imports": [], + "exports": [{ "ident": "calculateTotal" }, { "ident": "multiply" }], + }, + "logger.ts": { + "imports": [], + "exports": [{ "ident": "logger" }], + }, + }); + }); +});