Skip to content

Commit

Permalink
Add support for importing SVG, PNG, and JPEG files directly (#2284)
Browse files Browse the repository at this point in the history
This adds support for importing SVG, PNG, and JPEG files directly in a
Snap. For example, you can now use this:

```ts
import file from './path/to/image.png';
```

And use that image in a snap UI using the image component:

```ts
content: image(file)
```

Internally the image is converted to an SVG string by the CLI, which
makes this possible.

---------

Co-authored-by: Frederik Bolding <[email protected]>
  • Loading branch information
Mrtenz and FrederikBolding authored Mar 18, 2024
1 parent 2933da0 commit 8669459
Show file tree
Hide file tree
Showing 18 changed files with 709 additions and 36 deletions.
2 changes: 1 addition & 1 deletion packages/examples/packages/images/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "UKBWUM+VMGi3YPuP2zhT3fpfB5MgN7muuU2MBaW5964=",
"shasum": "6hmcWrwBG05b5UlKIzJppBhfqqi+C/YxFSQgJrFjh64=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions packages/examples/packages/images/src/images/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
68 changes: 62 additions & 6 deletions packages/examples/packages/images/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describe('onRpcRequest', () => {

describe('getQrCode', () => {
it('generates a QR code for the given data', async () => {
const { request, close } = await installSnap();
const { request } = await installSnap();

const response = request({
method: 'getQrCode',
Expand All @@ -46,16 +46,14 @@ describe('onRpcRequest', () => {
await ui.ok();

expect(await response).toRespondWith(null);

await close();
});
});

describe('getCat', () => {
// This test is flaky so we disable it for now
// This test is flaky, so we disable it for now.
// eslint-disable-next-line jest/no-disabled-tests
it.skip('shows a cat', async () => {
const { request, close } = await installSnap();
const { request } = await installSnap();

const response = request({
method: 'getCat',
Expand Down Expand Up @@ -84,8 +82,66 @@ describe('onRpcRequest', () => {
await ui.ok();

expect(await response).toRespondWith(null);
});
});

describe('getSvgIcon', () => {
it('shows an SVG icon', async () => {
const { request } = await installSnap();

const response = request({
method: 'getSvgIcon',
});

const ui = await response.getInterface();
// eslint-disable-next-line jest/prefer-strict-equal
expect(ui.content).toEqual({
type: 'panel',
children: [
{
type: 'text',
value: 'Here is an SVG icon:',
},
{
type: 'image',
value: expect.stringContaining('<svg'),
},
],
});

await ui.ok();

expect(await response).toRespondWith(null);
});
});

describe('getPngIcon', () => {
it('shows a PNG icon', async () => {
const { request } = await installSnap();

await close();
const response = request({
method: 'getPngIcon',
});

const ui = await response.getInterface();
// eslint-disable-next-line jest/prefer-strict-equal
expect(ui.content).toEqual({
type: 'panel',
children: [
{
type: 'text',
value: 'Here is a PNG icon:',
},
{
type: 'image',
value: expect.stringContaining('data:image/png;base64'),
},
],
});

await ui.ok();

expect(await response).toRespondWith(null);
});
});
});
29 changes: 29 additions & 0 deletions packages/examples/packages/images/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import {
} from '@metamask/snaps-sdk';
import { renderSVG } from 'uqr';

import pngIcon from './images/icon.png';
import svgIcon from './images/icon.svg';

/**
* The parameters for the `getQrCode` method.
*
Expand Down Expand Up @@ -74,6 +77,32 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ request }) => {
});
}

case 'getSvgIcon': {
return await snap.request({
method: 'snap_dialog',
params: {
type: DialogType.Alert,

// `.svg` files are imported as strings, so they can be used directly
// with the `image` component.
content: panel([text('Here is an SVG icon:'), image(svgIcon)]),
},
});
}

case 'getPngIcon': {
return await snap.request({
method: 'snap_dialog',
params: {
type: DialogType.Alert,

// `.png` files are imported as SVGs containing an `<image>` tag,
// so they can be used directly with the `image` component.
content: panel([text('Here is a PNG icon:'), image(pngIcon)]),
},
});
}

default:
throw new MethodNotFoundError({ method: request.method });
}
Expand Down
30 changes: 29 additions & 1 deletion packages/snaps-cli/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { dim } from 'chalk';
import { readFile } from 'fs/promises';
import Module from 'module';
import { basename, dirname, resolve } from 'path';
import type { Infer } from 'superstruct';
import {
array,
boolean,
Expand All @@ -30,6 +29,7 @@ import {
unknown,
empty,
} from 'superstruct';
import type { Infer } from 'superstruct';
import type { Configuration as WebpackConfiguration } from 'webpack';

import { TranspilationModes } from './builders';
Expand Down Expand Up @@ -415,6 +415,27 @@ export type SnapWebpackConfig = {
zlib?: boolean;
};

/**
* Optional features to enable in the CLI.
*
* @example
* {
* features: {
* images: true,
* }
* }
*/
features?: {
/**
* Whether to enable support for images. If `true`, the Webpack
* configuration will be modified to support images. If `false`, the
* Webpack configuration will not be modified.
*
* @default true
*/
images?: boolean;
};

/**
* A function to customize the Webpack configuration used to build the snap.
* This function will be called with the default Webpack configuration, and
Expand Down Expand Up @@ -618,6 +639,13 @@ export const SnapsWebpackConfigStruct = object({
false,
),

features: defaulted(
object({
images: defaulted(boolean(), true),
}),
{},
),

customizeWebpackConfig: optional(
SnapsWebpackCustomizeWebpackConfigFunctionStruct,
),
Expand Down
2 changes: 1 addition & 1 deletion packages/snaps-cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ export type {
} from './webpack';

// Re-exported from `snaps-cli` for convenience.
export { merge } from 'webpack-merge';
export { merge, mergeWithCustomize, mergeWithRules } from 'webpack-merge';
Loading

0 comments on commit 8669459

Please sign in to comment.