Skip to content

Commit adad84d

Browse files
authored
add asset import (#15)
1 parent bd6f70f commit adad84d

28 files changed

+297
-35
lines changed

docs/.vitepress/config.ts

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export default defineConfig({
5555
items: [
5656
{ text: "Introduction", link: "/guide/" },
5757
{ text: "Getting Started", link: "/guide/getting-started" },
58+
{ text: "Asset Import", link: "/guide/asset-import" },
5859
{ text: "GPU Acceleration", link: "/guide/gpu-acceleration" },
5960
{ text: "Plugins", link: "/guide/plugins" }
6061
]

docs/guide/asset-import.md

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Asset Import
2+
3+
By default, imgit is set up to detect and transform Markdown syntax in the source content. This works best for simple documentation and blog websites, but may not be flexible enough for more complex apps authored with frameworks like React.
4+
5+
To better fit component-based apps, imgit allows importing media assets with `import` statement to manually author the desired HTML.
6+
7+
Use `imgit:` namespace when importing a media asset to make imgit optimize it and return sources of the generated assets. For example, consider following [Astro](https://astro.build) page:
8+
9+
```astro
10+
---
11+
import psd from "imgit:https://example.com/photo.psd";
12+
import mkv from "imgit:/assets/video.mkv";
13+
---
14+
15+
<img src={psd.content.encoded}
16+
height={psd.info.height}
17+
loading="lazy"/>
18+
19+
<video src={mkv.content.encoded}
20+
poster={mkv.content.cover}
21+
height={mkv.info.height}
22+
autoplay loop/>
23+
```
24+
25+
Imported asset returns following default export:
26+
27+
```ts
28+
type AssetImport = {
29+
content: {
30+
encoded: string,
31+
dense?: string,
32+
cover?: string,
33+
safe?: string
34+
},
35+
info: {
36+
type: string,
37+
height: number,
38+
width: number,
39+
alpha: boolean
40+
}
41+
};
42+
```
43+
44+
— where `content` are the sources of the generated optimized files, which you can assign to the various `src` attributes of the built HTML. Additional `info` object contains metadata describing the imported asset, such its dimensions and MIME type, which may be helpful when building the host component.
45+
46+
::: tip
47+
When using TypeScript, add `/// <reference types="imgit/client" />` to a `.d.ts` file anywhere under project source directory to correctly resolve virtual asset imports.
48+
:::

docs/guide/getting-started.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ await fs.writeFile("./public/index.html", output);
115115
await exit();
116116
```
117117

118-
::: tip Example
118+
::: tip SAMPLE
119119
Find minimal sample on using imgit directly with Deno runtime on GitHub: https://github.com/elringus/imgit/tree/main/samples/minimal.
120120
:::
121121

docs/guide/integrations/astro.md

+48-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,53 @@ export default defineConfig({
2525

2626
:::
2727

28-
::: tip Sample
28+
When building the project, imgit will automatically transform image Markdown syntax
29+
into optimized HTML. For example, given following `index.md` page:
30+
31+
```md
32+
# PSD Image
33+
![](https://example.com/photo.psd)
34+
35+
# MKV Video
36+
![](/assets/video.mkv)
37+
38+
# YouTube Video
39+
![](https://www.youtube.com/watch?v=arbuYnJoLtU)
40+
```
41+
42+
— imgit will produce following HTML output:
43+
44+
```html
45+
<h1>PSD Image</h1>
46+
<picture><source srcset="optimized-source.avif"></picture>
47+
48+
<h1>MKV Video</h1>
49+
<video src="optimized-source.av1"></video>
50+
51+
<h1>YouTube Video</h1>
52+
<div>optimized YouTube player</div>
53+
```
54+
55+
In case you'd like to instead manually build the HTML (eg, with custom components), import the media assets with `imgit:` namespace:
56+
57+
```astro
58+
---
59+
import psd from "imgit:https://example.com/photo.psd";
60+
import mkv from "imgit:/assets/video.mkv";
61+
---
62+
63+
<img src={psd.content.encoded}
64+
height={psd.info.height}
65+
loading="lazy"/>
66+
67+
<video src={mkv.content.encoded}
68+
poster={mkv.content.cover}
69+
height={mkv.info.height}
70+
autoplay loop/>
71+
```
72+
73+
When using TypeScript, add `/// <reference types="imgit/client" />` to a `.d.ts` file anywhere inside project source directory to correctly resolve virtual asset imports.
74+
75+
::: tip SAMPLE
2976
https://github.com/elringus/imgit/tree/main/samples/astro
3077
:::

docs/guide/integrations/nuxt.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ export default defineNuxtConfig({
2323

2424
:::
2525

26-
::: tip Sample
26+
::: tip SAMPLE
2727
https://github.com/elringus/imgit/tree/main/samples/nuxt
2828
:::

docs/guide/integrations/remix.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@ export default defineConfig({
2626

2727
:::
2828

29-
::: tip Sample
29+
::: tip SAMPLE
3030
https://github.com/elringus/imgit/tree/main/samples/remix
3131
:::

docs/guide/integrations/solid.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@ export default defineConfig({
2626

2727
:::
2828

29-
::: tip Sample
29+
::: tip SAMPLE
3030
https://github.com/elringus/imgit/tree/main/samples/solid
3131
:::

docs/guide/integrations/svelte.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,6 @@ export default defineConfig({
4040

4141
:::
4242

43-
::: tip Sample
43+
::: tip SAMPLE
4444
https://github.com/elringus/imgit/tree/main/samples/svelte
4545
:::

docs/guide/integrations/vite.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ export default defineConfig({
2525

2626
:::
2727

28-
::: tip Sample
28+
::: tip SAMPLE
2929
https://github.com/elringus/imgit/tree/main/samples/vite
3030
:::

docs/guide/integrations/vitepress.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,6 @@ export default { extends: { Layout: DefaultTheme.Layout } };
4242

4343
:::
4444

45-
::: tip Sample
45+
::: tip SAMPLE
4646
https://github.com/elringus/imgit/tree/main/samples/vitepress
4747
:::

docs/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@
1010
"typescript": "^5.3.3",
1111
"vitepress": "^1.0.0-rc.42",
1212
"typedoc-vitepress-theme": "^1.0.0-next.9",
13-
"imgit": "^0.1.3"
13+
"imgit": "^0.2.1"
1414
}
1515
}

package.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
{
22
"name": "imgit",
3-
"version": "0.1.3",
3+
"version": "0.2.1",
44
"description": "Transform images, video and YouTube links to HTML optimized for web vitals.",
55
"author": "Elringus (https://elringus.me)",
66
"license": "MIT",
77
"keywords": ["CLS", "lazy-load", "embed", "size", "encode", "compress", "md", "avif", "vite-plugin"],
8-
"repository": { "type": "git", "url": "https://github.com/elringus/imgit.git" },
8+
"repository": { "type": "git", "url": "git+https://github.com/elringus/imgit.git" },
99
"funding": "https://github.com/sponsors/elringus",
1010
"homepage": "https://imgit.dev",
1111
"bugs": { "url": "https://github.com/elringus/imgit/issues" },
@@ -31,7 +31,7 @@
3131
},
3232
"devDependencies": {
3333
"typescript": "^5.3.3",
34-
"vitest": "^1.1.3",
35-
"@vitest/coverage-v8": "^1.1.3"
34+
"vitest": "^1.2.2",
35+
"@vitest/coverage-v8": "^1.2.2"
3636
}
3737
}

samples/astro/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ Example on plugging imgit to [astro](https://astro.build) web framework:
1010
> [!IMPORTANT]
1111
> Initial build could take up to 5 minutes for all the sample assets referenced in index.astro to fetch and encode. The files will be stored under `public` directory and consequent runs won't incur additional processing time.
1212
13-
Examine `src/pages/index.astro` and `astro.config.mts` sources for details.
13+
Examine `src/pages/index.astro` (markdown source transform), `src/pages/import.astro` (manual asset import) and `astro.config.mts` sources for details.

samples/astro/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"preview": "astro preview"
66
},
77
"dependencies": {
8-
"astro": "^4.1.1",
9-
"imgit": "^0.1.2"
8+
"astro": "^4.3.5",
9+
"imgit": "^0.2.1"
1010
}
1111
}

samples/astro/src/env.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
/// <reference types="astro/client" />
2+
/// <reference types="imgit/client" />

samples/astro/src/pages/import.astro

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
import psd from "imgit:https://github.com/elringus/imgit/raw/main/samples/assets/psd.psd";
3+
import mkv from "imgit:https://github.com/elringus/imgit/raw/main/samples/assets/mkv.mkv";
4+
---
5+
6+
<html lang="en">
7+
8+
<head>
9+
<title>Astro Import Sample</title>
10+
<meta charset="utf-8">
11+
<link rel="icon" href="data:,">
12+
<style is:global>
13+
body { background: #222; }
14+
img, video { max-width: 100%; height: auto; }
15+
</style>
16+
</head>
17+
18+
<body>
19+
20+
<img src={psd.content.encoded}
21+
height={psd.info.height}
22+
loading="lazy"/>
23+
24+
<video src={mkv.content.encoded}
25+
poster={mkv.content.cover}
26+
height={mkv.info.height}
27+
autoplay loop/>
28+
29+
</body>
30+
31+
</html>

samples/astro/src/pages/index.astro

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
<body>
1414

15+
<a href="/import">Import Sample</a>
16+
1517
<!-- This file will be transformed by imgit when bundling with vite. Markdown image
1618
tags below will be replaced with <picture> and <video> HTML tags referencing
1719
generated content. Transformed files will be written under 'public' directory. -->

scripts/build.sh

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
rm -rf dist
22
tsc --build src
3+
cp src/client.d.ts dist
34
cp src/client/styles.css dist/client
45
cp src/plugin/youtube/styles.css dist/plugin/youtube

src/client.d.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/* v8 ignore start */
2+
declare module "imgit:*" {
3+
const asset: import("./server/import.js").AssetImport;
4+
export default asset;
5+
}

src/plugin/vite.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Platform, Prefs, Plugin, boot, exit, transform, std } from "../server/index.js";
1+
import { Platform, Prefs, Plugin, boot, exit, transform, std, loader } from "../server/index.js";
22

33
/** Configures vite plugin behaviour. */
44
export type VitePrefs = Prefs & {
@@ -17,8 +17,10 @@ export type VitePlugin = {
1717
transformIndexHtml: {
1818
order: "pre" | "post",
1919
handler: (html: string, ctx: { filename: string }) => Promise<{ html: string, tags: HtmlTag[] }>
20-
}
20+
};
2121
buildEnd: (error?: Error) => Promise<void> | void;
22+
resolveId: (source: string) => string | null;
23+
load: (id: string) => Promise<string> | null;
2224
};
2325

2426
// https://vitejs.dev/guide/api-plugin#transformindexhtml
@@ -45,7 +47,9 @@ export default function (prefs?: VitePrefs, platform?: Platform): VitePlugin {
4547
tags: !prefs || prefs.inject !== false ? inject(<never>prefs?.plugins) : []
4648
})
4749
},
48-
buildEnd: exit
50+
buildEnd: exit,
51+
resolveId: (source) => loader.isImgitAssetImport(source) ? source : null,
52+
load: (id) => loader.isImgitAssetImport(id) ? loader.importImgitAsset(id) : null
4953
};
5054
}
5155

src/server/import.ts

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { stages } from "./transform/index.js";
2+
import { EncodedContent, ContentInfo, BuiltAsset } from "./asset.js";
3+
4+
/** Result of importing asset via imgit. */
5+
export type AssetImport = {
6+
/** Sources of the asset content. */
7+
content: EncodedContent;
8+
/** Content metadata. */
9+
info: ContentInfo;
10+
}
11+
12+
/** Whether specified import identifier is an imgit asset import. */
13+
export function isImgitAssetImport(importId: string): boolean {
14+
return importId.startsWith("imgit:");
15+
}
16+
17+
/** Resolves result (source code) of importing an imgit asset. */
18+
export async function importImgitAsset(importId: string): Promise<string> {
19+
const url = importId.substring(6);
20+
const asset = <BuiltAsset>{ syntax: { text: "", index: -1, url } };
21+
stages.resolve.asset(asset);
22+
await stages.fetch.asset(asset);
23+
await stages.probe.asset(asset);
24+
await stages.encode.asset(asset);
25+
const size = stages.build.size(asset);
26+
return `export default {
27+
content: {
28+
encoded: ${buildSrc(asset.content.encoded)},
29+
dense: ${buildSrc(asset.content.dense)},
30+
cover: ${buildSrc(asset.content.cover)},
31+
safe: ${buildSrc(asset.content.safe)}
32+
},
33+
info: {
34+
type: "${asset.content.info.type}",
35+
height: ${size.height},
36+
width: ${size.width},
37+
alpha: ${asset.content.info.alpha}
38+
}
39+
}`;
40+
}
41+
42+
function buildSrc(path?: string) {
43+
if (path === undefined) return "undefined";
44+
const src = stages.build.source(path);
45+
return `"${src}"`;
46+
}

src/server/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export { Plugin, PluginInjection } from "./config/plugin.js";
88
export { ctx } from "./context.js";
99
export { Cache, cache } from "./cache.js";
1010
export { stages, transform } from "./transform/index.js";
11+
export * as loader from "./import.js";
1112
export * from "./config/index.js";
1213
export * from "./asset.js";
1314

src/server/transform/5-encode.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ export async function encodeAll(assets: ProbedAsset[]): Promise<EncodedAsset[]>
88
await everythingIsFetched();
99
for (const asset of assets)
1010
if (!(await encodeWithPlugins(<EncodedAsset>asset)))
11-
await encodeAsset(<EncodedAsset>asset);
11+
await encode(<EncodedAsset>asset);
1212
return <EncodedAsset[]>assets;
1313
}
1414

1515
/** Encodes asset content with ffmpeg. */
16-
export async function encodeAsset(asset: EncodedAsset): Promise<void> {
16+
export async function encode(asset: EncodedAsset): Promise<void> {
1717
await encodeMain(asset.content, asset);
1818
await encodeSafe(asset.content, asset);
1919
await encodeDense(asset.content, asset);

0 commit comments

Comments
 (0)