|
| 1 | +--- |
| 2 | +description: | |
| 3 | + The Builder class is used to generate optimized assets for production. |
| 4 | +--- |
| 5 | + |
| 6 | +> [warn]: The `Builder` class was used during the alpha phase of Fresh 2 before |
| 7 | +> the Fresh vite plugin was released. You can skip this page if you're using |
| 8 | +> vite. |
| 9 | +
|
| 10 | +The `Builder` class is used to generate production assets of your app. You'll |
| 11 | +typically find it being created inside your project's `dev.ts` file. |
| 12 | + |
| 13 | +```ts dev.ts |
| 14 | +import { Builder } from "fresh/dev"; |
| 15 | + |
| 16 | +const builder = new Builder({ target: "safari12" }); |
| 17 | + |
| 18 | +if (Deno.args.includes("build")) { |
| 19 | + // This creates a production build |
| 20 | + await builder.build(); |
| 21 | +} else { |
| 22 | + // This starts a development server with live reload |
| 23 | + await builder.listen(() => import("./main.ts")); |
| 24 | +} |
| 25 | +``` |
| 26 | + |
| 27 | +## Options |
| 28 | + |
| 29 | +You can customize the builder by passing options. |
| 30 | + |
| 31 | +```ts dev.ts |
| 32 | +const builder = new Builder({ |
| 33 | + // Browser target for generated code. Maps to https://esbuild.github.io/api/#target |
| 34 | + target?: string | string[]; |
| 35 | + // The root directory of the project. All other paths will be resolved |
| 36 | + // against this if they're relative. (Default: `Deno.cwd()`) |
| 37 | + root?: string; |
| 38 | + // The path to your server entry point. (Default: `<root>/main.ts`) |
| 39 | + serverEntry?: string; |
| 40 | + // Where to write generated files when doing a production build. |
| 41 | + // (default: `<root>/_fresh/`) |
| 42 | + outDir?: string; |
| 43 | + // Path to static file directory. (Default: `<root>/static/`) |
| 44 | + staticDir?: string; |
| 45 | + // Path to island directory. (Default: `<root>/islands`) |
| 46 | + islandDir?: string; |
| 47 | + // Path to routes directory. (Default: `<root>/routes`) |
| 48 | + routeDir?: string; |
| 49 | + // File paths which should be ignored |
| 50 | + ignore?: RegExp[]; |
| 51 | + // Optionally generate production source maps |
| 52 | + // See https://esbuild.github.io/api/#source-maps |
| 53 | + sourceMap?: { |
| 54 | + kind?: boolean | 'linked' | 'inline' | 'external' | 'both'; |
| 55 | + sourceRoot?: string; |
| 56 | + sourcesContent?: boolean; |
| 57 | + }; |
| 58 | +}) |
| 59 | +``` |
| 60 | + |
| 61 | +## Registering islands |
| 62 | + |
| 63 | +The builder is where you'll register files that contain islands. This is the |
| 64 | +same API that Fresh uses internally. |
| 65 | + |
| 66 | +```ts dev.ts |
| 67 | +const builder = new Builder(); |
| 68 | + |
| 69 | +// Path to local island |
| 70 | +builder.registerIsland("path/to/my/Island.tsx"); |
| 71 | +// File urls work too |
| 72 | +builder.registerIsland("file:///path/to/my/Island.tsx"); |
| 73 | +// Also islands from jsr |
| 74 | +builder.registerIsland("jsr:@marvinh-test/fresh-island"); |
| 75 | +``` |
| 76 | + |
| 77 | +## Adding build plugins |
| 78 | + |
| 79 | +The `Builder` has a very simple processing mechanism for static files. |
| 80 | + |
| 81 | +```ts dev.ts |
| 82 | +builder.onTransformStaticFile({ |
| 83 | + pluginName: "My cool plugin", |
| 84 | + filter: /\.css$/, |
| 85 | +}, (args) => { |
| 86 | + // Prepend `body { background: red }` to every `.css` file |
| 87 | + const code = `body { background: red } ${args.text}`; |
| 88 | + |
| 89 | + return { |
| 90 | + content: code, |
| 91 | + map: undefined, // Optional: source maps |
| 92 | + }; |
| 93 | +}); |
| 94 | +``` |
| 95 | + |
| 96 | +> [info]: Only static files in `static/` or the value you set `staticDir` to |
| 97 | +> will be processed. The builder won't process anything else. |
| 98 | +
|
| 99 | +## Testing |
| 100 | + |
| 101 | +Testing applications with the `Builder` class involves creating a build snapshot |
| 102 | +and assigning that to each app instance. |
| 103 | + |
| 104 | +```ts my-app.test.ts |
| 105 | +// Best to do this once instead of for every test case for |
| 106 | +// performance reasons. |
| 107 | +const builder = new Builder(); |
| 108 | +const applySnapshot = await builder.build({ snapshot: "memory" }); |
| 109 | + |
| 110 | +function testApp() { |
| 111 | + const app = new App() |
| 112 | + .get("/", () => new Response("hello")) |
| 113 | + .fsRoutes(); |
| 114 | + |
| 115 | + // Applies build snapshot to this app instance. |
| 116 | + applySnapshot(app); |
| 117 | + return app; |
| 118 | +} |
| 119 | + |
| 120 | +Deno.test("My Test", async () => { |
| 121 | + const handler = testApp().handler(); |
| 122 | + |
| 123 | + const response = await handler(new Request("http://localhost")); |
| 124 | + const text = await response.text(); |
| 125 | + |
| 126 | + if (text !== "hello") { |
| 127 | + throw new Error("fail"); |
| 128 | + } |
| 129 | +}); |
| 130 | +``` |
| 131 | + |
| 132 | +## Tailwindcss |
| 133 | + |
| 134 | +[Tailwindcss](https://tailwindcss.com/) is a utility-first CSS framework that |
| 135 | +generates CSS out of the class names that are used in JSX. Since we use |
| 136 | +Tailwindcss ourselves here at Deno, Fresh ships with an official plugin for |
| 137 | +that. |
| 138 | + |
| 139 | +### Usage |
| 140 | + |
| 141 | +1. Set `nodeModulesDir` in `deno.json` to `"auto"` or `"manual"` |
| 142 | + |
| 143 | +```diff deno.json |
| 144 | + { |
| 145 | + "name": "@example/my-cool-project" |
| 146 | ++ "nodeModulesDir": "auto", |
| 147 | + "imports": { |
| 148 | + ... |
| 149 | + } |
| 150 | + } |
| 151 | +``` |
| 152 | + |
| 153 | +2. Run `deno install jsr:@fresh/plugin-tailwind` |
| 154 | +3. Update `dev.ts`: |
| 155 | + |
| 156 | +```diff dev.ts |
| 157 | + import { Builder } from "fresh/dev"; |
| 158 | ++ import { tailwind } from "@fresh/plugin-tailwind"; |
| 159 | + |
| 160 | + const builder = new Builder(); |
| 161 | ++ tailwind(builder); |
| 162 | +``` |
| 163 | + |
| 164 | +4. Add `@import "tailwindcss";` at the top of your main stylesheet. |
| 165 | + |
| 166 | +For more information on how to use tailwindcss, check out |
| 167 | +[their documentation](https://tailwindcss.com/docs/styling-with-utility-classes). |
| 168 | + |
| 169 | +### Options |
| 170 | + |
| 171 | +You can customize the tailwind plugin via the following options: |
| 172 | + |
| 173 | +```ts dev.ts |
| 174 | +tailwind(builder, app, { |
| 175 | + // Exclude certain files from processing |
| 176 | + exclude: ["/admin/**", "*.temp.css"], |
| 177 | + // Force optimization (defaults to production mode) |
| 178 | + optimize: true, |
| 179 | + // Exclude base styles |
| 180 | + base: null, |
| 181 | +}); |
| 182 | +``` |
| 183 | + |
| 184 | +### Tailwindcss v3 |
| 185 | + |
| 186 | +If can't update to the current version of tailwindcss we have a dedicated |
| 187 | +`@fresh/plugin-tailwindcss-v3` plugin that uses tailwindcss v3. That way you can |
| 188 | +decided on your own when it's best to update to v4. |
| 189 | + |
| 190 | +```ts dev.ts |
| 191 | +import { Builder } from "fresh/dev"; |
| 192 | +import { tailwind } from "@fresh/plugin-tailwind-v3"; |
| 193 | + |
| 194 | +tailwind(builder, {}); |
| 195 | +``` |
0 commit comments