SwiftyTailwind is a Swift Package to lazily download and run the Tailwind CLI from a Swift project (e.g. Vapor app or Publish project).
First, you need to add SwiftyTailwind
as a dependency in your project's Package.swift
:
.package(url: "https://github.com/tuist/SwiftyTailwind.git", .upToNextMinor(from: "0.5.0"))
Once added, you'll create an instance of SwiftyTailwind
specifying the version you'd like to use and where you'd like it to be downloaded.
let tailwind = SwiftyTailwind(version: .latest, directory: "./cache")
If you don't pass any argument, it defaults to the latest version in the system's default temporary directory. If you work in a team, we recommend fixing the version to minimize non-determinism across environments.
tailwind.config.js
You can create a tailwind.config.js
configuration file by running the initialize
function on the SwiftyTailwind
instance:
try await tailwind.initialize()
Check out all the available options in the documentation.
To run Tailwind against a project, you can use the run
function:
try await subject.run(input: inputCSSPath, output: outputCSSPath, options: .content("views/**/*.html"))
If you'd like Tailwind to keep watching for file changes, you can pass the .watch
option:
try await subject.run(input: inputCSSPath,
output: outputCSSPath,
options: .watch, .content("views/**/*.html"))
Check out all the available options in the documentation.
You can integrate this with Vapor by setting up a tailwind.swift
:
import SwiftyTailwind
import TSCBasic
import Vapor
func tailwind(_ app: Application) async throws {
let resourcesDirectory = try AbsolutePath(validating: app.directory.resourcesDirectory)
let publicDirectory = try AbsolutePath(validating: app.directory.publicDirectory)
let tailwind = SwiftyTailwind()
try await tailwind.run(
input: .init(validating: "Styles/app.css", relativeTo: resourcesDirectory),
output: .init(validating: "styles/app.generated.css", relativeTo: publicDirectory),
options: .content("\(app.directory.viewsDirectory)**/*.leaf")
)
}
Then in configure.swift
:
try await tailwind(app)
app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
And in your index.leaf
:
<link rel="stylesheet" href="https://github.com/tuist/SwiftyTailwind/blob/main/styles/app.generated.css" />
It can be desirable for Tailwind to watch and rebuild changes without restarting the Vapor server. It is also best to restrict this behavior for development only.
You can integrate this behavior by setting up a tailwind.swift
:
#if DEBUG
import SwiftyTailwind
import TSCBasic
import Vapor
func runTailwind(_ app: Application) async throws {
let resourcesDirectory = try AbsolutePath(validating: app.directory.resourcesDirectory)
let publicDirectory = try AbsolutePath(validating: app.directory.publicDirectory)
let tailwind = SwiftyTailwind()
async let runTailwind: () = tailwind.run(
input: .init(validating: "Styles/app.css", relativeTo: resourcesDirectory),
output: .init(validating: "styles/app.generated.css", relativeTo: publicDirectory),
options: .watch, .content("\(app.directory.viewsDirectory)**/*.leaf"))
return try await runTailwind
}
#endif
and then in entrypoint.swift
, replace try await app.execute()
with:
#if DEBUG
if (env.arguments.contains { arg in arg == "migrate" }) {
try await app.execute()
} else {
async let runApp: () = try await app.execute()
_ = await [try runTailwind(app), try await runApp]
}
#else
try await app.execute()
#endif
The check for migrate
in the arguments will ensure that it doesn't run when doing migrations in development.
Additionally, it may be a good idea to setup a script to minify the CSS before deploying to production.
Thanks goes to these wonderful people (emoji key):
Chris 🖋 |
William Sedlacek 📖 |
Brady Klein 💻 |
This project follows the all-contributors specification. Contributions of any kind welcome!