diff --git a/src/__tests__/vendor/tailwind/_tailwind.tsx b/src/__tests__/vendor/tailwind/_tailwind.tsx index 1011808e..098b6aab 100644 --- a/src/__tests__/vendor/tailwind/_tailwind.tsx +++ b/src/__tests__/vendor/tailwind/_tailwind.tsx @@ -1,5 +1,7 @@ import type { PropsWithChildren, ReactElement } from "react"; +import { inspect } from "node:util"; + import tailwind from "@tailwindcss/postcss"; import { screen, @@ -30,7 +32,7 @@ export type NativewindRenderOptions = RenderOptions & { /** Whether to include the plugin in the generated CSS. @default true */ plugin?: boolean; /** Enable debug logging. @default false - Set process.env.NATIVEWIND_TEST_AUTO_DEBUG and run tests with the node inspector */ - debug?: boolean; + debug?: boolean | "verbose"; }; const debugDefault = Boolean(process.env.NODE_OPTIONS?.includes("--inspect")); @@ -69,7 +71,7 @@ export async function render( css += `\n${extraCss}`; } - if (debug) { + if (debug === "verbose") { console.log(`Input CSS:\n---\n${css}\n---\n`); } @@ -87,6 +89,16 @@ export async function render( const compiled = registerCSS(output, { debug: false }); + if (debug) { + console.log( + inspect(compiled.stylesheet(), { + colors: true, + compact: false, + depth: null, + }), + ); + } + return Object.assign( {}, tlRender(component, { diff --git a/src/__tests__/vendor/tailwind/layout.test.tsx b/src/__tests__/vendor/tailwind/layout.test.tsx index 5bbd4b2f..9e63f076 100644 --- a/src/__tests__/vendor/tailwind/layout.test.tsx +++ b/src/__tests__/vendor/tailwind/layout.test.tsx @@ -393,32 +393,42 @@ describe("Layout - Isolation", () => { describe("Layout - Object Fit", () => { test("object-contain", async () => { expect(await renderCurrentTest()).toStrictEqual({ - props: {}, - warnings: { properties: ["object-fit"] }, + props: { + contentFit: "contain", + style: {}, + }, }); }); test("object-cover", async () => { expect(await renderCurrentTest()).toStrictEqual({ - props: {}, - warnings: { properties: ["object-fit"] }, + props: { + contentFit: "cover", + style: {}, + }, }); }); test("object-fill", async () => { expect(await renderCurrentTest()).toStrictEqual({ - props: {}, - warnings: { properties: ["object-fit"] }, + props: { + contentFit: "fill", + style: {}, + }, }); }); test("object-none", async () => { expect(await renderCurrentTest()).toStrictEqual({ - props: {}, - warnings: { properties: ["object-fit"] }, + props: { + contentFit: "none", + style: {}, + }, }); }); test("object-scale-down", async () => { expect(await renderCurrentTest()).toStrictEqual({ - props: {}, - warnings: { properties: ["object-fit"] }, + props: { + contentFit: "scale-down", + style: {}, + }, }); }); }); @@ -426,56 +436,74 @@ describe("Layout - Object Fit", () => { describe("Layout - Object Position", () => { test("object-bottom", async () => { expect(await renderCurrentTest()).toStrictEqual({ - props: {}, - warnings: { properties: ["object-position"] }, + props: { + contentPosition: "bottom", + style: {}, + }, }); }); test("object-center", async () => { expect(await renderCurrentTest()).toStrictEqual({ - props: {}, - warnings: { properties: ["object-position"] }, + props: { + contentPosition: "center", + style: {}, + }, }); }); test("object-left", async () => { expect(await renderCurrentTest()).toStrictEqual({ - props: {}, - warnings: { properties: ["object-position"] }, + props: { + contentPosition: "left", + style: {}, + }, }); }); test("object-left-bottom", async () => { expect(await renderCurrentTest()).toStrictEqual({ - props: {}, - warnings: { properties: ["object-position"] }, + props: { + contentPosition: "left bottom", + style: {}, + }, }); }); test("object-left-top", async () => { expect(await renderCurrentTest()).toStrictEqual({ - props: {}, - warnings: { properties: ["object-position"] }, + props: { + contentPosition: "left top", + style: {}, + }, }); }); test("object-right", async () => { expect(await renderCurrentTest()).toStrictEqual({ - props: {}, - warnings: { properties: ["object-position"] }, + props: { + contentPosition: "right", + style: {}, + }, }); }); test("object-right-bottom", async () => { expect(await renderCurrentTest()).toStrictEqual({ - props: {}, - warnings: { properties: ["object-position"] }, + props: { + contentPosition: "right bottom", + style: {}, + }, }); }); test("object-right-top", async () => { expect(await renderCurrentTest()).toStrictEqual({ - props: {}, - warnings: { properties: ["object-position"] }, + props: { + contentPosition: "right top", + style: {}, + }, }); }); test("object-top", async () => { expect(await renderCurrentTest()).toStrictEqual({ - props: {}, - warnings: { properties: ["object-position"] }, + props: { + contentPosition: "top", + style: {}, + }, }); }); }); diff --git a/src/compiler/declarations.ts b/src/compiler/declarations.ts index ebbd9bb1..0e18eab8 100644 --- a/src/compiler/declarations.ts +++ b/src/compiler/declarations.ts @@ -7,6 +7,7 @@ import type { BorderStyle, ColorOrAuto, CssColor, + CustomProperty, Declaration, DimensionPercentageFor_LengthValue, EnvironmentVariable, @@ -975,6 +976,12 @@ export function parseCustomDeclaration( parseUnparsed(declaration.value.value, builder, property) === "borderless", ); + } else if (property === "object-fit") { + // https://github.com/parcel-bundler/lightningcss/issues/1046 + parseObjectFit(declaration.value, builder); + } else if (property === "object-position") { + // https://github.com/parcel-bundler/lightningcss/issues/1047 + parseObjectPosition(declaration.value, builder); } else if ( validProperties.has(property) || property.startsWith("--") || @@ -2997,6 +3004,33 @@ function parseGradientItem( } } +function parseObjectFit( + declaration: CustomProperty, + builder: StylesheetBuilder, +) { + builder.addMapping({ + "object-fit": "contentFit", + }); + builder.addDescriptor( + "object-fit", + parseUnparsed(declaration.value, builder, "object-fit"), + ); +} + +function parseObjectPosition( + declaration: CustomProperty, + builder: StylesheetBuilder, +) { + builder.addMapping({ + "object-position": "contentPosition", + }); + builder.addDescriptor("object-position", [ + {}, + "join", + [parseUnparsed(declaration.value, builder, "object-position"), " "], + ]); +} + function parseVisibility( declaration: DeclarationType<"visibility">, builder: StylesheetBuilder, diff --git a/src/runtime/native/styles/functions/index.ts b/src/runtime/native/styles/functions/index.ts index 94ffe907..fe2ec44c 100644 --- a/src/runtime/native/styles/functions/index.ts +++ b/src/runtime/native/styles/functions/index.ts @@ -2,4 +2,5 @@ export * from "./animation-timing"; export * from "./calc"; export * from "./filters"; export * from "./platform-functions"; +export * from "./string-functions"; export * from "./transform-functions"; diff --git a/src/runtime/native/styles/functions/string-functions.ts b/src/runtime/native/styles/functions/string-functions.ts new file mode 100644 index 00000000..e4c741c9 --- /dev/null +++ b/src/runtime/native/styles/functions/string-functions.ts @@ -0,0 +1,22 @@ +import type { StyleFunctionResolver } from "../resolve"; + +export const join: StyleFunctionResolver = (resolveValue, value) => { + const args = resolveValue(value[2]); + + if (!Array.isArray(args)) { + return args; + } + + const array: unknown = args[0]; + const separator: unknown = args[1] ?? ","; + + if (!Array.isArray(array)) { + return array; + } + + if (!separator || typeof separator !== "string") { + return array.join(); + } + + return array.join(separator); +};