feat: Discover icons from the public directory (#72)
This commit is contained in:
+14
-8
@@ -47,9 +47,9 @@ The [manifest's `version` and `version_name`](https://developer.chrome.com/docs/
|
||||
}
|
||||
```
|
||||
|
||||
## Icon
|
||||
## `icons`
|
||||
|
||||
The [manifest's `icons`](https://developer.chrome.com/docs/extensions/mv3/manifest/icons/) property needs to be set in the config file. The files should be added to WXT's [`public` directory](/get-started/assets#public-directory).
|
||||
By default, WXT will discover icons in your [`public` directory](/get-started/assets#public-directory) and use them for the [manifest's `icons`](https://developer.chrome.com/docs/extensions/mv3/manifest/icons/).
|
||||
|
||||
```
|
||||
public/
|
||||
@@ -60,21 +60,27 @@ public/
|
||||
└─ icon-128.png
|
||||
```
|
||||
|
||||
Icon files need to match the following regex to be automatically included in the manifest. Most design software can output icons in one of these formats
|
||||
|
||||
<<< @/../src/core/utils/manifest.ts#snippet
|
||||
|
||||
If you prefer to use filenames in a different format, you can add the icons manually in your `wxt.config.ts` file:
|
||||
|
||||
```ts
|
||||
export default defineConfig({
|
||||
manifest: {
|
||||
icons: {
|
||||
16: '/icon-16.png',
|
||||
24: '/icon-24.png',
|
||||
48: '/icon-48.png',
|
||||
96: '/icon-96.png',
|
||||
128: '/icon-128.png',
|
||||
16: '/extension-icon-16.png',
|
||||
24: '/extension-icon-24.png',
|
||||
48: '/extension-icon-48.png',
|
||||
96: '/extension-icon-96.png',
|
||||
128: '/extension-icon-128.png',
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Permissions
|
||||
## `permissions`
|
||||
|
||||
[Permissions](https://developer.chrome.com/docs/extensions/reference/permissions/) must be listed in the manifest config.
|
||||
|
||||
|
||||
@@ -133,6 +133,66 @@ describe('Manifest Content', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('icons', () => {
|
||||
it('should auto-discover icons with the correct name', async () => {
|
||||
const project = new TestProject();
|
||||
project.addFile('public/icon-16.png');
|
||||
project.addFile('public/icon/32.jpeg');
|
||||
project.addFile('public/icon@48w.jpg');
|
||||
project.addFile('public/icon-64x64.gif');
|
||||
project.addFile('public/icon@96.bmp');
|
||||
project.addFile('public/icon/128x128.ico');
|
||||
|
||||
await project.build();
|
||||
const manifest = await project.getOutputManifest();
|
||||
|
||||
expect(manifest.icons).toEqual({
|
||||
'16': 'icon-16.png',
|
||||
'32': 'icon/32.jpeg',
|
||||
'48': 'icon@48w.jpg',
|
||||
'64': 'icon-64x64.gif',
|
||||
'96': 'icon@96.bmp',
|
||||
'128': 'icon/128x128.ico',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return undefined when no icons are found', async () => {
|
||||
const project = new TestProject();
|
||||
project.addFile('public/logo.png');
|
||||
project.addFile('public/icon.jpeg');
|
||||
|
||||
await project.build();
|
||||
const manifest = await project.getOutputManifest();
|
||||
|
||||
expect(manifest.icons).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should allow icons to be overwritten from the wxt.config.ts file', async () => {
|
||||
const project = new TestProject();
|
||||
project.addFile('public/icon-16.png');
|
||||
project.addFile('public/icon-32.png');
|
||||
project.addFile('public/logo-16.png');
|
||||
project.addFile('public/logo-32.png');
|
||||
project.addFile('public/logo-48.png');
|
||||
|
||||
const icons = {
|
||||
'16': 'logo-16.png',
|
||||
'32': 'logo-32.png',
|
||||
'48': 'logo-48.png',
|
||||
};
|
||||
project.setConfigFileConfig({
|
||||
manifest: {
|
||||
icons,
|
||||
},
|
||||
});
|
||||
|
||||
await project.build();
|
||||
const manifest = await project.getOutputManifest();
|
||||
|
||||
expect(manifest.icons).toEqual(icons);
|
||||
});
|
||||
});
|
||||
|
||||
it('should group content scripts and styles together based on their matches and run_at', async () => {
|
||||
const project = new TestProject();
|
||||
project.addFile(
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
mapWxtOptionsToContentScript,
|
||||
} from './content-scripts';
|
||||
import { getPackageJson } from './package';
|
||||
import { normalizePath } from './paths';
|
||||
|
||||
/**
|
||||
* Writes the manifest to the output directory and the build output.
|
||||
@@ -65,6 +66,7 @@ export async function generateMainfest(
|
||||
? pkg?.version
|
||||
: undefined,
|
||||
short_name: pkg?.shortName,
|
||||
icons: discoverIcons(buildOutput),
|
||||
},
|
||||
config.manifest,
|
||||
);
|
||||
@@ -317,6 +319,40 @@ function addEntrypoints(
|
||||
}
|
||||
}
|
||||
|
||||
function discoverIcons(
|
||||
buildOutput: Omit<BuildOutput, 'manifest'>,
|
||||
): Manifest.WebExtensionManifest['icons'] {
|
||||
const icons: [string, string][] = [];
|
||||
// prettier-ignore
|
||||
// #region snippet
|
||||
const iconRegex = [
|
||||
/^icon-([0-9]+)\.(png|bmp|jpeg|jpg|ico|gif)$/, // icon-16.png
|
||||
/^icon-([0-9]+)x[0-9]+\.(png|bmp|jpeg|jpg|ico|gif)$/, // icon-16x16.png
|
||||
/^icon@([0-9]+)w\.(png|bmp|jpeg|jpg|ico|gif)$/, // icon@16w.png
|
||||
/^icon@([0-9]+)h\.(png|bmp|jpeg|jpg|ico|gif)$/, // icon@16h.png
|
||||
/^icon@([0-9]+)\.(png|bmp|jpeg|jpg|ico|gif)$/, // icon@16.png
|
||||
/^icon[\/\\]([0-9]+)\.(png|bmp|jpeg|jpg|ico|gif)$/, // icon/16.png
|
||||
/^icon[\/\\]([0-9]+)x[0-9]+\.(png|bmp|jpeg|jpg|ico|gif)$/, // icon/16x16.png
|
||||
];
|
||||
// #endregion snippet
|
||||
|
||||
buildOutput.publicAssets.forEach((asset) => {
|
||||
let size: string | undefined;
|
||||
for (const regex of iconRegex) {
|
||||
const match = asset.fileName.match(regex);
|
||||
if (match?.[1] != null) {
|
||||
size = match[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (size == null) return;
|
||||
|
||||
icons.push([size, normalizePath(asset.fileName)]);
|
||||
});
|
||||
|
||||
return icons.length > 0 ? Object.fromEntries(icons) : undefined;
|
||||
}
|
||||
|
||||
function addDevModeCsp(
|
||||
manifest: Manifest.WebExtensionManifest,
|
||||
config: InternalConfig,
|
||||
|
||||
Reference in New Issue
Block a user