Skip to content

Commit

Permalink
add asset import (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
elringus authored Feb 8, 2024
1 parent bd6f70f commit adad84d
Show file tree
Hide file tree
Showing 28 changed files with 297 additions and 35 deletions.
1 change: 1 addition & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export default defineConfig({
items: [
{ text: "Introduction", link: "/guide/" },
{ text: "Getting Started", link: "/guide/getting-started" },
{ text: "Asset Import", link: "/guide/asset-import" },
{ text: "GPU Acceleration", link: "/guide/gpu-acceleration" },
{ text: "Plugins", link: "/guide/plugins" }
]
Expand Down
48 changes: 48 additions & 0 deletions docs/guide/asset-import.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Asset Import

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.

To better fit component-based apps, imgit allows importing media assets with `import` statement to manually author the desired HTML.

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:

```astro
---
import psd from "imgit:https://example.com/photo.psd";
import mkv from "imgit:/assets/video.mkv";
---
<img src={psd.content.encoded}
height={psd.info.height}
loading="lazy"/>
<video src={mkv.content.encoded}
poster={mkv.content.cover}
height={mkv.info.height}
autoplay loop/>
```

Imported asset returns following default export:

```ts
type AssetImport = {
content: {
encoded: string,
dense?: string,
cover?: string,
safe?: string
},
info: {
type: string,
height: number,
width: number,
alpha: boolean
}
};
```

— 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.

::: tip
When using TypeScript, add `/// <reference types="imgit/client" />` to a `.d.ts` file anywhere under project source directory to correctly resolve virtual asset imports.
:::
2 changes: 1 addition & 1 deletion docs/guide/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ await fs.writeFile("./public/index.html", output);
await exit();
```

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

Expand Down
49 changes: 48 additions & 1 deletion docs/guide/integrations/astro.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,53 @@ export default defineConfig({

:::

::: tip Sample
When building the project, imgit will automatically transform image Markdown syntax
into optimized HTML. For example, given following `index.md` page:

```md
# PSD Image
![](https://example.com/photo.psd)

# MKV Video
![](/assets/video.mkv)

# YouTube Video
![](https://www.youtube.com/watch?v=arbuYnJoLtU)
```

— imgit will produce following HTML output:

```html
<h1>PSD Image</h1>
<picture><source srcset="optimized-source.avif"></picture>

<h1>MKV Video</h1>
<video src="optimized-source.av1"></video>

<h1>YouTube Video</h1>
<div>optimized YouTube player</div>
```

In case you'd like to instead manually build the HTML (eg, with custom components), import the media assets with `imgit:` namespace:

```astro
---
import psd from "imgit:https://example.com/photo.psd";
import mkv from "imgit:/assets/video.mkv";
---
<img src={psd.content.encoded}
height={psd.info.height}
loading="lazy"/>
<video src={mkv.content.encoded}
poster={mkv.content.cover}
height={mkv.info.height}
autoplay loop/>
```

When using TypeScript, add `/// <reference types="imgit/client" />` to a `.d.ts` file anywhere inside project source directory to correctly resolve virtual asset imports.

::: tip SAMPLE
https://github.com/elringus/imgit/tree/main/samples/astro
:::
2 changes: 1 addition & 1 deletion docs/guide/integrations/nuxt.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ export default defineNuxtConfig({

:::

::: tip Sample
::: tip SAMPLE
https://github.com/elringus/imgit/tree/main/samples/nuxt
:::
2 changes: 1 addition & 1 deletion docs/guide/integrations/remix.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ export default defineConfig({

:::

::: tip Sample
::: tip SAMPLE
https://github.com/elringus/imgit/tree/main/samples/remix
:::
2 changes: 1 addition & 1 deletion docs/guide/integrations/solid.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ export default defineConfig({

:::

::: tip Sample
::: tip SAMPLE
https://github.com/elringus/imgit/tree/main/samples/solid
:::
2 changes: 1 addition & 1 deletion docs/guide/integrations/svelte.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ export default defineConfig({

:::

::: tip Sample
::: tip SAMPLE
https://github.com/elringus/imgit/tree/main/samples/svelte
:::
2 changes: 1 addition & 1 deletion docs/guide/integrations/vite.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ export default defineConfig({

:::

::: tip Sample
::: tip SAMPLE
https://github.com/elringus/imgit/tree/main/samples/vite
:::
2 changes: 1 addition & 1 deletion docs/guide/integrations/vitepress.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ export default { extends: { Layout: DefaultTheme.Layout } };

:::

::: tip Sample
::: tip SAMPLE
https://github.com/elringus/imgit/tree/main/samples/vitepress
:::
2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@
"typescript": "^5.3.3",
"vitepress": "^1.0.0-rc.42",
"typedoc-vitepress-theme": "^1.0.0-next.9",
"imgit": "^0.1.3"
"imgit": "^0.2.1"
}
}
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "imgit",
"version": "0.1.3",
"version": "0.2.1",
"description": "Transform images, video and YouTube links to HTML optimized for web vitals.",
"author": "Elringus (https://elringus.me)",
"license": "MIT",
"keywords": ["CLS", "lazy-load", "embed", "size", "encode", "compress", "md", "avif", "vite-plugin"],
"repository": { "type": "git", "url": "https://github.com/elringus/imgit.git" },
"repository": { "type": "git", "url": "git+https://github.com/elringus/imgit.git" },
"funding": "https://github.com/sponsors/elringus",
"homepage": "https://imgit.dev",
"bugs": { "url": "https://github.com/elringus/imgit/issues" },
Expand All @@ -31,7 +31,7 @@
},
"devDependencies": {
"typescript": "^5.3.3",
"vitest": "^1.1.3",
"@vitest/coverage-v8": "^1.1.3"
"vitest": "^1.2.2",
"@vitest/coverage-v8": "^1.2.2"
}
}
2 changes: 1 addition & 1 deletion samples/astro/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ Example on plugging imgit to [astro](https://astro.build) web framework:
> [!IMPORTANT]
> 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.
Examine `src/pages/index.astro` and `astro.config.mts` sources for details.
Examine `src/pages/index.astro` (markdown source transform), `src/pages/import.astro` (manual asset import) and `astro.config.mts` sources for details.
4 changes: 2 additions & 2 deletions samples/astro/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"preview": "astro preview"
},
"dependencies": {
"astro": "^4.1.1",
"imgit": "^0.1.2"
"astro": "^4.3.5",
"imgit": "^0.2.1"
}
}
1 change: 1 addition & 0 deletions samples/astro/src/env.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/// <reference types="astro/client" />
/// <reference types="imgit/client" />
31 changes: 31 additions & 0 deletions samples/astro/src/pages/import.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
import psd from "imgit:https://github.com/elringus/imgit/raw/main/samples/assets/psd.psd";
import mkv from "imgit:https://github.com/elringus/imgit/raw/main/samples/assets/mkv.mkv";
---

<html lang="en">

<head>
<title>Astro Import Sample</title>
<meta charset="utf-8">
<link rel="icon" href="data:,">
<style is:global>
body { background: #222; }
img, video { max-width: 100%; height: auto; }
</style>
</head>

<body>

<img src={psd.content.encoded}
height={psd.info.height}
loading="lazy"/>

<video src={mkv.content.encoded}
poster={mkv.content.cover}
height={mkv.info.height}
autoplay loop/>

</body>

</html>
2 changes: 2 additions & 0 deletions samples/astro/src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

<body>

<a href="/import">Import Sample</a>

<!-- This file will be transformed by imgit when bundling with vite. Markdown image
tags below will be replaced with <picture> and <video> HTML tags referencing
generated content. Transformed files will be written under 'public' directory. -->
Expand Down
1 change: 1 addition & 0 deletions scripts/build.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
rm -rf dist
tsc --build src
cp src/client.d.ts dist
cp src/client/styles.css dist/client
cp src/plugin/youtube/styles.css dist/plugin/youtube
5 changes: 5 additions & 0 deletions src/client.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/* v8 ignore start */
declare module "imgit:*" {
const asset: import("./server/import.js").AssetImport;
export default asset;
}
10 changes: 7 additions & 3 deletions src/plugin/vite.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Platform, Prefs, Plugin, boot, exit, transform, std } from "../server/index.js";
import { Platform, Prefs, Plugin, boot, exit, transform, std, loader } from "../server/index.js";

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

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

Expand Down
46 changes: 46 additions & 0 deletions src/server/import.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { stages } from "./transform/index.js";
import { EncodedContent, ContentInfo, BuiltAsset } from "./asset.js";

/** Result of importing asset via imgit. */
export type AssetImport = {
/** Sources of the asset content. */
content: EncodedContent;
/** Content metadata. */
info: ContentInfo;
}

/** Whether specified import identifier is an imgit asset import. */
export function isImgitAssetImport(importId: string): boolean {
return importId.startsWith("imgit:");
}

/** Resolves result (source code) of importing an imgit asset. */
export async function importImgitAsset(importId: string): Promise<string> {
const url = importId.substring(6);
const asset = <BuiltAsset>{ syntax: { text: "", index: -1, url } };
stages.resolve.asset(asset);
await stages.fetch.asset(asset);
await stages.probe.asset(asset);
await stages.encode.asset(asset);
const size = stages.build.size(asset);
return `export default {
content: {
encoded: ${buildSrc(asset.content.encoded)},
dense: ${buildSrc(asset.content.dense)},
cover: ${buildSrc(asset.content.cover)},
safe: ${buildSrc(asset.content.safe)}
},
info: {
type: "${asset.content.info.type}",
height: ${size.height},
width: ${size.width},
alpha: ${asset.content.info.alpha}
}
}`;
}

function buildSrc(path?: string) {
if (path === undefined) return "undefined";
const src = stages.build.source(path);
return `"${src}"`;
}
1 change: 1 addition & 0 deletions src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export { Plugin, PluginInjection } from "./config/plugin.js";
export { ctx } from "./context.js";
export { Cache, cache } from "./cache.js";
export { stages, transform } from "./transform/index.js";
export * as loader from "./import.js";
export * from "./config/index.js";
export * from "./asset.js";

Expand Down
4 changes: 2 additions & 2 deletions src/server/transform/5-encode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ export async function encodeAll(assets: ProbedAsset[]): Promise<EncodedAsset[]>
await everythingIsFetched();
for (const asset of assets)
if (!(await encodeWithPlugins(<EncodedAsset>asset)))
await encodeAsset(<EncodedAsset>asset);
await encode(<EncodedAsset>asset);
return <EncodedAsset[]>assets;
}

/** Encodes asset content with ffmpeg. */
export async function encodeAsset(asset: EncodedAsset): Promise<void> {
export async function encode(asset: EncodedAsset): Promise<void> {
await encodeMain(asset.content, asset);
await encodeSafe(asset.content, asset);
await encodeDense(asset.content, asset);
Expand Down
Loading

0 comments on commit adad84d

Please sign in to comment.