Working with assets
Thanks to its solid Vite foundations, you have plenty of ways to work with any kind of static or optimized assets, from fonts to WASM.
Scripts and styles via HTML tags
There are multiple cases for using HTML tags for importing client-side JS or CSS:
1. Globally, at the document level
When you are associating multiple routes with the same enclosing document, you might want to inject shared assets for them.
<link rel="stylesheet" href="/src/document.scss" />
<script type="module" src="/src/document.client.ts"></script>
You can always add more link or script, but it’s probably cleaner to add CSS
@import or JS import in single entry points, for you own sources or those
from vendors.
Important
While it is strongly recommended to use .js even for TS, and relative
paths, not absolute, these rules won’t apply in HTML when using Vite /
Rollup based systems for bundle entrypoints. You have to provide the full
real path relative to the project root, like
/src/components/my-element.ts.
Same for non-vanilla CSS.
Alternatively, you can use this notation, with URL and import.meta.url:
new URLvar URL: new (url: string | URL, base?: string | URL) => URLThe URL interface is used to parse, construct, normalize, and encode URLs. It works by providing properties which allow you to easily read and modify the components of a URL.
('./my-asset.css', import.meta.urlImportMeta.url: string ).pathnameURL.pathname: stringThe pathname property of the URL interface represents a location in a hierarchical structure. It is a string constructed from a list of path segments, each of which is prefixed by a / character.
;
E.g.
📄 /src/document.ts
import { htmlimport html } from '@gracile/gracile/server-html';
export const documentconst document: any = htmlimport html `
<head>
...
<link
rel="stylesheet"
href=${new URLvar URL: new (url: string | URL, base?: string | URL) => URLThe URL interface is used to parse, construct, normalize, and encode URLs. It works by providing properties which allow you to easily read and modify the components of a URL.
('./document.css', import.meta.urlImportMeta.url: string ).pathnameURL.pathname: stringThe pathname property of the URL interface represents a location in a hierarchical structure. It is a string constructed from a list of path segments, each of which is prefixed by a / character.
}
/>
<script
type="module"
src=${new URLvar URL: new (url: string | URL, base?: string | URL) => URLThe URL interface is used to parse, construct, normalize, and encode URLs. It works by providing properties which allow you to easily read and modify the components of a URL.
('./document.client.ts', import.meta.urlImportMeta.url: string ).pathnameURL.pathname: stringThe pathname property of the URL interface represents a location in a hierarchical structure. It is a string constructed from a list of path segments, each of which is prefixed by a / character.
}
></script>
...
</head>
`;
It’s arguably more verbose but it is also a more canonical way to achieve this
endeavour.
Dealing with relative paths can be preferable, too.
For both dev. and build, Vite will resolve those paths for you properly.
This works, too, for explicit entry point grouping:
<script type="module">
import '/src/document.client.ts';
import '/src/my-lib.ts';
</script>
Note that Vite will deduplicate/bundle per-route assets properly for build. E.g.
<script type="module" src="/src/alpha.ts"></script>
<script type="module" src="/src/beta.ts"></script>
<script type="module" src="..."></script>
It becomes one after build
<script type="module" src="/assets/page-bundle-123h4sh.js"></script>
2. Per-route assets injection
📄 /src/document.ts
import { htmlimport html } from '@gracile/gracile/server-html';
export const documentconst document: () => any = () => htmlimport html `
<!doctype html>
<html lang="en">
<head>
...
Route sibling page assets will be appended here, right before the closing HEAD
</head>
...
</html>
`;
Given:
- project/
- src/
- routes/
- my-page.ts
- my-page.css (or
.{scss,less,…}) - my-page.client.ts
- routes/
- …
- src/
Result in:
<link rel="stylesheet" href="/src/routes/my-page.css" />
<script type="module" src="/src/routes/my-page.client.ts"></script>
This mechanism isn’t here just for convenience, but to make to streamline the
way the server handler will be generated (but it’s also useful for static
mode).
Putting all route assets at the document level allows for deterministic
optimizations by the bundler at build time, which wouldn’t be possible (at least
easily) at run time.
3. Anywhere in a server template
You can also put script (module or inline) and link anywhere in your HTML
partials or server-rendered Lit Elements, but it’s not a recommended way.
First of all, it makes static analysis harder, for things like modules
preloader. Also, it’s generally recommended to put assets in <head> for
performance reasons, not in semi-random places in the body.
This rule is not set in stone, though. You might have legitimate reasons for
doing otherwise.
For static build, Vite will handle this just fine and apply optimizations on the
whole page at once, it will even put modules (async) in the <head> for you,
with preloaders.
However for server output, since your page output is not deterministic, those
will not be hoisted in the head.
You’ll have to make sure to use the “URL + import.meta.url” syntax, not
/src/foo/bar.ts; That way, the correct path will be dynamically injected in
place when building the Gracile server handler for production, hence, matching
the dev. behavior.
The public/ folder for static assets
As you know, Gracile is built on top of Vite, so naturally, it will inherit
Vite’s capabilities like serving assets as-is, from the public directory.
Putting an asset in it, will make it available from the base URL, e.g.,
/public/favicon.svg → /favicon.svg.
More details on the Vite docs.
Exotic file formats
For anything outside the realm of HTML/CSS/JS, you have a large collection of Vite plugins to cater to your needs.
Vite provides ?raw or ?inline flags, web workers loading mechanisms etc.
Gracile support this, naturally, but also provides a handful of plugins
specifically tailored to html template literals.