Skip to content

Commit 17a9c23

Browse files
committed
draft lume 3
1 parent 439f38f commit 17a9c23

File tree

3 files changed

+354
-2
lines changed

3 files changed

+354
-2
lines changed

deno.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
"cms": "deno task lume cms"
77
},
88
"imports": {
9-
"lume/": "https://deno.land/x/[email protected].0/",
10-
"lume/cms/": "https://cdn.jsdelivr.net/gh/lumeland/[email protected].0/",
9+
"lume/": "https://deno.land/x/[email protected].1/",
10+
"lume/cms/": "https://cdn.jsdelivr.net/gh/lumeland/[email protected].2/",
1111
"blog/": "https://deno.land/x/[email protected]/"
1212
},
1313
"compilerOptions": {

posts/lume-2.5.0-pedro-munho.md

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ comments:
88
date: '2025-01-11T00:00:00.000Z'
99
draft: false
1010
---
11+
1112
**_Feliz aninovo_ 🎄!**
1213

1314
New year and new Lume version! This time, I'd like to dedicate it to Pedro Días

posts/lume-3.md

+351
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,351 @@
1+
---
2+
title: Lume 3
3+
author: Óscar Otero
4+
draft: true
5+
tags:
6+
- Releases
7+
comments: {}
8+
---
9+
10+
## About loaded and copied files
11+
12+
When Lume builds a site, some files are **loaded** and others **copied**.
13+
14+
Files with extensions `.vto`, `.md`, or `.page.ts` are loaded because Lume needs
15+
to read the content to generate HTML pages. They are known as _Page files_.
16+
17+
Files added using the function `site.copy()` are just copied from the `src`
18+
folder to `dest` without reading the content, which is faster and consumes less
19+
memory. These files are known as _Static files_.
20+
21+
However, there are some files that must be loaded or copied, depending on the
22+
configuration. For example, CSS files are loaded if you use a plugin like
23+
Postcss, because the content needs to be processed. In this case, the plugin
24+
internally runs `site.loadAssets([".css"])`, to instruct Lume that the files
25+
with extension `.css` need to be loaded but not to generate HTML pages but
26+
assets.
27+
28+
One of the most recurrent issues is when a file is configured to be copied and
29+
loaded. For example:
30+
31+
```js
32+
site.copy([".css"]);
33+
site.use(postcss());
34+
```
35+
36+
In this configuration, the build copies all CSS files and you may expect these
37+
files to be transformed with the postcss plugin. However due to the `.css` files
38+
being copied, not loaded, **the plugin doesn't process them**.
39+
40+
Another example, a bit less obvious is when you have a folder with different
41+
files, some of them need to be processed and others don't:
42+
43+
```js
44+
site.copy("/assets");
45+
site.use(postcss());
46+
```
47+
48+
In this case, we are copying all files in the `/assets` folder. This folder can
49+
contain CSS files that, as you may guess, won't be processed by Postcss.
50+
51+
This behavior is confusing, but fixing it is a breaking change, so this is the
52+
main reason version 3 was released. Lume should be clever enough to don't
53+
delegate the decision of whether a file must be loaded or copied to you.
54+
55+
## Introducing `add()`
56+
57+
In Lume 3 all this logic was refactored and the functions `site.loadAssets()`
58+
and `site.copy()` were replaced with a single function: `site.add()`.
59+
60+
The `add()` function simply instructs Lume that you want to include this file in
61+
your site, but without specifying how this file must be treated. Lume will load
62+
the file if it needs to (for example, if it needs to be processed), or will copy
63+
it if no transformation needs to be made.
64+
65+
This simplifies a lot the configuration, especially in those cases where copied
66+
and loaded files are mixed in the same folder.
67+
68+
`site.add()` has the same syntax as the old `site.copy()`, so you can add all
69+
CSS files that are loaded and processed by Postcss:
70+
71+
```js
72+
site.add([".css"]);
73+
site.use(postcss());
74+
```
75+
76+
You can add also specific files and folders, and even change the destination
77+
folder:
78+
79+
```js
80+
// Add all files in /assets to the root of dest folder.
81+
site.add("/assets", ".");
82+
83+
// Run Postcss. Any CSS file in /assets will be processed!
84+
site.use(postcss());
85+
```
86+
87+
### Replacing `copyRemainingFiles()`
88+
89+
In Lume 2 there was the `site.copyRemainingFiles()` function as a way to manage
90+
complex situations like this. For example, let's say you have the following
91+
structure:
92+
93+
```txt
94+
|_ articles/
95+
|_ article-1/
96+
| |_ index.md
97+
| |_ picture.jpg
98+
| |_ document.pdf
99+
| |_ foo32.gif
100+
|_ article-2/
101+
|_ index.md
102+
|_ journey.mp4
103+
|_ download.zip
104+
```
105+
106+
In this structure we want to render all markdown files and copy the remaining
107+
files. In Lume 2 we cannot do `site.copy("articles")`, because the `index.md`
108+
files inside these folders wouldn't be processed (they would be treated as
109+
static files). So we had to use the `copyRemainingFiles` function.
110+
111+
```js
112+
// Lume 2
113+
site.copyRemainingFiles(
114+
(path: string) => path.startsWith("/articles/"),
115+
);
116+
```
117+
118+
With this configuration, any file not loaded that is inside the `/articles`
119+
folder will be copied.
120+
121+
In Lume 3, we can simply do:
122+
123+
```js
124+
// Lume 3
125+
site.add("articles");
126+
```
127+
128+
**And that's all!** Any file that must be loaded (like `.md` files) will be
129+
loaded. And the others simply will be copied.
130+
131+
### Copy remote files
132+
133+
`site.add()` not only can add files from the `src` folder but also remote files.
134+
In Lume 2 this was possible using `remoteFile` function:
135+
136+
```js
137+
// Lume 2
138+
site.remoteFile("styles.css", "https://example.com/theme/styles.css");
139+
site.copy("styles.css");
140+
```
141+
142+
Lume 3 makes this use case more simple:
143+
144+
```js
145+
// Lume 3
146+
site.add("https://example.com/theme/styles.css", "styles.css");
147+
```
148+
149+
The `site.add()` function accepts also `npm` specifiers:
150+
151+
```js
152+
site.add("npm:normalize.css", "/styles/normalize.css");
153+
```
154+
155+
Internally, uses jsDelivr to download the file. In this example,
156+
`npm:normalize.css` is transformed to
157+
`https://cdn.jsdelivr.net/npm/normalize.css`.
158+
159+
> [!note]
160+
>
161+
> `site.remoteFile` is still useful in Lume 3, especially for other file types
162+
> like `_data` files, templates, components, etc.
163+
164+
## Add files explicitly
165+
166+
In Lume 2, some plugins configure Lume to load files with a certain extension
167+
automatically. For example, Postcss configures Lume to load all CSS files:
168+
169+
```js
170+
// Lume 2
171+
172+
// All .css files are loaded and processed
173+
site.use(postcss());
174+
```
175+
176+
In most cases, this is what you want. But if you don't want to output all CSS
177+
files, you have to use the `ignore()` function.
178+
179+
For example, let's say we have the following structure and only want to process
180+
`homepage.css` and `about.css` but not the files inside `utils` (because they
181+
are already imported):
182+
183+
```txt
184+
|_ styles/
185+
|_ homepage.css
186+
|_ about.css
187+
|_ utils/
188+
|_ typography.css
189+
|_ color.css
190+
```
191+
192+
In Lume 2 we have to ignore this folder.
193+
194+
```js
195+
// Lume 2
196+
site.use(postcss());
197+
site.ignore("styles/utils");
198+
```
199+
200+
The problem with this behavior is that Lume by default adds everything and then
201+
you have to ignore what you don't want. Sometimes this approach causes more harm
202+
than benefit.
203+
204+
In Lume 3, thanks to the `site.add()` function, it's very easy to add new files
205+
(and only the files that you want), so plugins **no longer load files by
206+
default**. You have to explicitly add them, which is more clear and intuitive:
207+
208+
```js
209+
// Lume 3
210+
211+
site.use(postcss());
212+
site.add("styles/homepage.css");
213+
site.add("styles/about.css");
214+
```
215+
216+
Or if you want to load all css files:
217+
218+
```js
219+
// Lume 3
220+
221+
site.use(postcss());
222+
site.add(".css");
223+
```
224+
225+
Another benefit is you have better control of all entry points of your assets.
226+
For example, for esbuild:
227+
228+
```js
229+
// Lume 3
230+
231+
site.use(esbuild());
232+
site.add("main.ts"); // Only this file will be bundled
233+
```
234+
235+
This change affects to the following plugins: `svgo`, `transform_images`,
236+
`picture`, `postcss`, `sass`, `tailwindcss`, `unocss`, `esbuild` and `terser`.
237+
238+
## One JSX
239+
240+
Lume started supporting `JSX` as a template engine thanks to the `jsx` plugin
241+
that uses React under the hood. The reason for choosing React is because it's
242+
the default library, so it worked without any configuration.
243+
244+
Some versions later, Lume added support for Preact, a smaller and more
245+
performant React alternative, with the plugin `jsx_preact`. Having two JSX
246+
plugins for the same purpose is not good, and adds unnecessary complexity (for
247+
example to use MDX plugin).
248+
249+
In addition to that, both libraries are frontend-first libraries, with features
250+
like hooks, events callbacks, hydration, etc, that are not supported on the
251+
server side, and some people were confused about what they are able to do with
252+
them in Lume.
253+
254+
Lume 3 has only one JSX library, and it's not React or Preact. It's
255+
[SSX](https://github.com/oscarotero/ssx/) a library created specifically for
256+
static sites, created in TypeScript, faster than React and Preact
257+
[See Benchmarks](https://github.com/oscarotero/ssx/actions/runs/13022300332/job/36325328553#step:7:22)
258+
and more ergonomic. It allows to create asynchronous components, insert raw code
259+
like `<!doctype html>`, and comes with great documentation including all HTML
260+
elements and attributes, with links to MDN.
261+
262+
And because Lume has only a JSX library, the MDX plugin works automatically
263+
without needing to use a JSX plugin before.
264+
265+
## Improved Lume components
266+
267+
### Async components
268+
269+
One of the main limitations of Lume components was the synchronous nature. The
270+
reason is to support JSX components that were synchronous in React and Preact.
271+
With SSX, we don't have this limitation any more and all components are async.
272+
273+
For example, you can create a component in JSX that returns a promise:
274+
275+
```jsx
276+
// _components/salute.jsx
277+
278+
export default async function ({ id }) {
279+
const response = await fetch(`https://example.com/api?id=${id}`);
280+
const data = await response.json();
281+
return <strong>Hello {data.name}</strong>;
282+
}
283+
```
284+
285+
And this component can be used in any other template engine, like JSX:
286+
287+
```jsx
288+
export default async function ({ comp }) {
289+
return (
290+
<p>
291+
<comp.Salute id="23" />
292+
</p>
293+
);
294+
}
295+
```
296+
297+
Or Vento:
298+
299+
```html
300+
<p>{{ await comp.Salute({ id: 23}) }}</p>
301+
```
302+
303+
### Folder component
304+
305+
Lume components not only generate HTML code to reuse but can also export the CSS
306+
and JS code needed to run this code on the browser. The code must be exported in
307+
the variables `css` and `js`. For example:
308+
309+
```md
310+
---
311+
css: |
312+
.mainTitle {
313+
color: red;
314+
}
315+
---
316+
317+
<h1 class="mainTitle">{{ name }}</h1>
318+
```
319+
320+
The problem of this approach is the CSS and JS code is not treated as CSS and JS
321+
code by the code editor, so there's no syntax highlight.
322+
323+
In Lume 3 it's possible to generate a component in a folder, with the CSS and JS
324+
code in different files. To do that, you have to keep the following structure:
325+
326+
```txt
327+
|_ _components/
328+
|_ button/
329+
|_ comp.vto
330+
|_ style.css
331+
|_ script.js
332+
```
333+
334+
Any folder containing a `comp.*` file will be loaded as a component using the
335+
folder name as the component name. And the files `style.css` and `script.js`
336+
will be loaded as the CSS and JS code for the component. This makes the creation
337+
of components more ergonomic, specially for cases with a lot of CSS and JS code.
338+
339+
Now you can use the component anywhere:
340+
341+
```js
342+
{
343+
{
344+
await comp.Button({ content: "click here" });
345+
}
346+
}
347+
```
348+
349+
## Tailwind 4
350+
351+
## Processors improvements

0 commit comments

Comments
 (0)