From e4bfa8c9b7d9812237eaba3bd5397613e266583c Mon Sep 17 00:00:00 2001 From: Philipp Spiess Date: Tue, 19 Nov 2024 16:19:08 +0100 Subject: [PATCH] Bundle core plugins with the standalone build (#15028) Closes #15012 We do not have replacements for these plugins _just yet_. In order to increase compatibility with setups that rely on some of these legacy plugins, this PR bundles `@tailwindcss/forms`, `@tailwindcss/typography`, and `@tailwindcss/aspect-ratio` (after https://github.com/tailwindlabs/tailwindcss/pull/15029) with the standalone build now. In comparison to v3, this omits the `@tailwindcss/container-queries` plugin since is not a first-party feature of Tailwind CSS v4. ## Test Plan Added an integration test. I also tested this by running the standalone binary in a temporary folder with as simple input css: ```css @import "tailwindcss"; @plugin "@tailwindcss/typography"; ``` --- CHANGELOG.md | 1 + integrations/cli/standalone.test.ts | 58 ++++++++++++++++ packages/@tailwindcss-node/src/compile.ts | 14 ++++ packages/@tailwindcss-standalone/package.json | 3 + packages/@tailwindcss-standalone/src/index.ts | 20 +++++- .../@tailwindcss-standalone/src/types.d.ts | 1 + pnpm-lock.yaml | 66 +++++++++++++++++++ 7 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 integrations/cli/standalone.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index d08332a10..976f56df0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Reintroduce `max-w-screen-*` utilities that read from the `--breakpoint` namespace as deprecated utilities ([#15013](https://github.com/tailwindlabs/tailwindcss/pull/15013)) - Support using CSS variables as arbitrary values without `var(…)` by using parentheses instead of square brackets (e.g. `bg-(--my-color)`) ([#15020](https://github.com/tailwindlabs/tailwindcss/pull/15020)) - Add new `in-*` variant ([#15025](https://github.com/tailwindlabs/tailwindcss/pull/15025)) +- Bundle `@tailwindcss/forms`, `@tailwindcss/typography`, and `@tailwindcss/aspect-ratio` with the standalone CLI ([#15028](https://github.com/tailwindlabs/tailwindcss/pull/15028)) - Allow `addUtilities()` and `addComponents()` to work with child combinators and other complex selectors ([#15029](https://github.com/tailwindlabs/tailwindcss/pull/15029)) - Support colors that use `` in JS configs and plugins ([#15033](https://github.com/tailwindlabs/tailwindcss/pull/15033)) - _Upgrade (experimental)_: Migrate `[&>*]` to the `*` variant ([#15022](https://github.com/tailwindlabs/tailwindcss/pull/15022)) diff --git a/integrations/cli/standalone.test.ts b/integrations/cli/standalone.test.ts new file mode 100644 index 000000000..a40d69fc9 --- /dev/null +++ b/integrations/cli/standalone.test.ts @@ -0,0 +1,58 @@ +import os from 'node:os' +import path from 'node:path' +import { candidate, css, html, json, test } from '../utils' + +const STANDALONE_BINARY = (() => { + switch (os.platform()) { + case 'win32': + return 'tailwindcss-windows-x64.exe' + case 'darwin': + return os.arch() === 'x64' ? 'tailwindcss-macos-x64' : 'tailwindcss-macos-arm64' + case 'linux': + return os.arch() === 'x64' ? 'tailwindcss-linux-x64' : 'tailwindcss-linux-arm64' + default: + throw new Error(`Unsupported platform: ${os.platform()} ${os.arch()}`) + } +})() + +test( + 'includes first-party plugins', + { + fs: { + 'package.json': json` + { + "dependencies": { + "tailwindcss": "workspace:^", + "@tailwindcss/cli": "workspace:^" + } + } + `, + 'index.html': html` +
+

Headline

+
+ +
+ `, + 'src/index.css': css` + @import 'tailwindcss/theme' theme(reference); + @import 'tailwindcss/utilities'; + + @plugin '@tailwindcss/forms'; + @plugin '@tailwindcss/typography'; + @plugin '@tailwindcss/aspect-ratio'; + `, + }, + }, + async ({ fs, exec }) => { + await exec( + `${path.resolve(__dirname, `../../packages/@tailwindcss-standalone/dist/${STANDALONE_BINARY}`)} --input src/index.css --output dist/out.css`, + ) + + await fs.expectFileToContain('dist/out.css', [ + candidate`form-input`, + candidate`prose`, + candidate`aspect-w-16`, + ]) + }, +) diff --git a/packages/@tailwindcss-node/src/compile.ts b/packages/@tailwindcss-node/src/compile.ts index 8a5f8ee20..60ddecff2 100644 --- a/packages/@tailwindcss-node/src/compile.ts +++ b/packages/@tailwindcss-node/src/compile.ts @@ -98,6 +98,7 @@ export async function loadModule(id: string, base: string, onDependency: (path: if (!resolvedPath) { throw new Error(`Could not resolve '${id}' from '${base}'`) } + let [module, moduleDependencies] = await Promise.all([ importModule(pathToFileURL(resolvedPath).href + '?id=' + Date.now()), getModuleDependencies(resolvedPath), @@ -140,6 +141,13 @@ async function loadStylesheet(id: string, base: string, onDependency: (path: str // can be resolved properly. let jiti: null | Jiti = null async function importModule(path: string): Promise { + if (typeof globalThis.__tw_load === 'function') { + let module = await globalThis.__tw_load(path) + if (module) { + return module + } + } + try { return await import(path) } catch (error) { @@ -174,6 +182,12 @@ const jsResolver = EnhancedResolve.ResolverFactory.createResolver({ }) function resolveJsId(id: string, base: string): Promise { + if (typeof globalThis.__tw_resolve === 'function') { + let resolved = globalThis.__tw_resolve(id, base) + if (resolved) { + return Promise.resolve(resolved) + } + } return runResolver(jsResolver, id, base) } diff --git a/packages/@tailwindcss-standalone/package.json b/packages/@tailwindcss-standalone/package.json index 0f180eb5c..4f702a535 100644 --- a/packages/@tailwindcss-standalone/package.json +++ b/packages/@tailwindcss-standalone/package.json @@ -25,7 +25,10 @@ "dist" ], "dependencies": { + "@tailwindcss/aspect-ratio": "^0.4.2", "@tailwindcss/cli": "workspace:^", + "@tailwindcss/forms": "^0.5.9", + "@tailwindcss/typography": "^0.5.15", "detect-libc": "1.0.3", "enhanced-resolve": "^5.17.1", "tailwindcss": "workspace:^" diff --git a/packages/@tailwindcss-standalone/src/index.ts b/packages/@tailwindcss-standalone/src/index.ts index ae90e2b6e..98e75c3dc 100644 --- a/packages/@tailwindcss-standalone/src/index.ts +++ b/packages/@tailwindcss-standalone/src/index.ts @@ -12,7 +12,10 @@ const localResolve = createRequire(import.meta.url).resolve globalThis.__tw_resolve = (id, baseDir) => { let isEmbeddedFileBase = baseDir === '/$bunfs/root' || baseDir?.includes(':/~BUN/root') const likelyEmbeddedFile = - id === 'tailwindcss' || id.startsWith('tailwindcss/') || isEmbeddedFileBase + id === 'tailwindcss' || + id.startsWith('tailwindcss/') || + id.startsWith('@tailwindcss/') || + isEmbeddedFileBase if (!likelyEmbeddedFile) { return false @@ -38,10 +41,25 @@ globalThis.__tw_resolve = (id, baseDir) => { case 'utilities': case 'utilities.css': return localResolve(utilitiesCss) + case '@tailwindcss/forms': + case '@tailwindcss/typography': + case '@tailwindcss/aspect-ratio': + return id default: return false } } +globalThis.__tw_load = async (id) => { + if (id.endsWith('@tailwindcss/forms')) { + return require('@tailwindcss/forms') + } else if (id.endsWith('@tailwindcss/typography')) { + return require('@tailwindcss/typography') + } else if (id.endsWith('@tailwindcss/aspect-ratio')) { + return require('@tailwindcss/aspect-ratio') + } else { + return undefined + } +} globalThis.__tw_version = packageJson.version globalThis.__tw_readFile = async (path, encoding) => { // When reading a file from the `$bunfs`, we need to use the synchronous diff --git a/packages/@tailwindcss-standalone/src/types.d.ts b/packages/@tailwindcss-standalone/src/types.d.ts index e13e4e427..3212ac5b6 100644 --- a/packages/@tailwindcss-standalone/src/types.d.ts +++ b/packages/@tailwindcss-standalone/src/types.d.ts @@ -8,3 +8,4 @@ declare var __tw_resolve: undefined | ((id: string, base?: string) => string | f declare var __tw_readFile: | undefined | ((path: string, encoding: BufferEncoding) => Promise) +declare var __tw_load: undefined | ((path: string) => Promise) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a19f30e2d..1a7b6627d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -213,9 +213,18 @@ importers: packages/@tailwindcss-standalone: dependencies: + '@tailwindcss/aspect-ratio': + specifier: ^0.4.2 + version: 0.4.2(tailwindcss@packages+tailwindcss) '@tailwindcss/cli': specifier: workspace:^ version: link:../@tailwindcss-cli + '@tailwindcss/forms': + specifier: ^0.5.9 + version: 0.5.9(tailwindcss@packages+tailwindcss) + '@tailwindcss/typography': + specifier: ^0.5.15 + version: 0.5.15(tailwindcss@packages+tailwindcss) detect-libc: specifier: 1.0.3 version: 1.0.3 @@ -1408,6 +1417,21 @@ packages: '@swc/helpers@0.5.2': resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} + '@tailwindcss/aspect-ratio@0.4.2': + resolution: {integrity: sha512-8QPrypskfBa7QIMuKHg2TA7BqES6vhBrDLOv8Unb6FcFyd3TjKbc6lcmb9UPQHxfl24sXoJ41ux/H7qQQvfaSQ==} + peerDependencies: + tailwindcss: '>=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1' + + '@tailwindcss/forms@0.5.9': + resolution: {integrity: sha512-tM4XVr2+UVTxXJzey9Twx48c1gcxFStqn1pQz0tRsX8o3DvxhN5oY5pvyAbUx7VTaZxpej4Zzvc6h+1RJBzpIg==} + peerDependencies: + tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20' + + '@tailwindcss/typography@0.5.15': + resolution: {integrity: sha512-AqhlCXl+8grUz8uqExv5OTtgpjuVIwFTSXTrh8y9/pw6q2ek7fJ+Y8ZEVw7EB2DCcuCOtEjf9w3+J3rzts01uA==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20' + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -2721,6 +2745,12 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.castarray@4.4.0: + resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -2771,6 +2801,10 @@ packages: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} + mini-svg-data-uri@1.4.4: + resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} + hasBin: true + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -3050,6 +3084,10 @@ packages: peerDependencies: postcss: ^8.2.14 + postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + postcss-selector-parser@6.1.2: resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} @@ -4361,6 +4399,23 @@ snapshots: dependencies: tslib: 2.8.0 + '@tailwindcss/aspect-ratio@0.4.2(tailwindcss@packages+tailwindcss)': + dependencies: + tailwindcss: link:packages/tailwindcss + + '@tailwindcss/forms@0.5.9(tailwindcss@packages+tailwindcss)': + dependencies: + mini-svg-data-uri: 1.4.4 + tailwindcss: link:packages/tailwindcss + + '@tailwindcss/typography@0.5.15(tailwindcss@packages+tailwindcss)': + dependencies: + lodash.castarray: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + postcss-selector-parser: 6.0.10 + tailwindcss: link:packages/tailwindcss + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.25.3 @@ -6047,6 +6102,10 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash.castarray@4.4.0: {} + + lodash.isplainobject@4.0.6: {} + lodash.merge@4.6.2: {} lodash.sortby@4.7.0: {} @@ -6090,6 +6149,8 @@ snapshots: mimic-fn@4.0.0: {} + mini-svg-data-uri@1.4.4: {} + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 @@ -6341,6 +6402,11 @@ snapshots: postcss: 8.4.47 postcss-selector-parser: 6.1.2 + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + postcss-selector-parser@6.1.2: dependencies: cssesc: 3.0.0