Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions packages/examples/packages/bip32/snap.manifest.dev.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"version": "2.3.0",
"description": "MetaMask example snap demonstrating the use of `snap_getBip32Entropy`.",
"proposedName": "BIP-32 Example Snap",
"repository": {
"type": "git",
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "yW/4a7WQ2dCmZHAaX6P2tihFF7azVPkpQ0iJlb/v61A=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
"packageName": "@metamask/bip32-example-snap",
"registry": "https://registry.npmjs.org"
}
}
},
"initialConnections": {
"http://localhost:9000": {}
},
"initialPermissions": {
"endowment:rpc": {
"dapps": true,
"snaps": true
},
"snap_dialog": {},
"snap_getBip32Entropy": [
{
"path": ["m", "44'", "0'"],
"curve": "secp256k1"
},
{
"path": ["m", "44'", "0'"],
"curve": "ed25519"
},
{
"path": ["m", "44'", "0'"],
"curve": "ed25519Bip32"
}
],
"snap_getBip32PublicKey": [
{
"path": ["m", "44'", "0'"],
"curve": "secp256k1"
}
]
},
"platformVersion": "10.3.0",
"manifestVersion": "0.1"
}
7 changes: 7 additions & 0 deletions packages/snaps-cli/src/builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ const builders = {
normalize: true,
},

manifest: {
alias: 'm',
describe: 'Path to snap.manifest.json file',
type: 'string',
normalize: true,
},

port: {
alias: 'p',
describe: 'Local server port for testing',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import type { SemVerVersion } from '@metamask/utils';
import normalFs from 'fs';
import ora from 'ora';
import { join } from 'path';

import { manifest } from './implementation';
import type * as webpack from '../../webpack';
Expand Down Expand Up @@ -74,7 +75,7 @@ describe('manifest', () => {

const spinner = ora();
const result = await manifest(
'/snap/snap.manifest.json',
join('/snap', 'snap.manifest.json'),
false,
undefined,
spinner,
Expand All @@ -97,7 +98,7 @@ describe('manifest', () => {

const spinner = ora();
const result = await manifest(
'/snap/snap.manifest.json',
join('/snap', 'snap.manifest.json'),
false,
undefined,
spinner,
Expand Down Expand Up @@ -132,7 +133,7 @@ describe('manifest', () => {

const spinner = ora();
const result = await manifest(
'/snap/snap.manifest.json',
join('/snap', 'snap.manifest.json'),
true,
undefined,
spinner,
Expand All @@ -155,7 +156,7 @@ describe('manifest', () => {
const log = jest.spyOn(console, 'log').mockImplementation();

await fs.writeFile(
'/snap/snap.manifest.json',
join('/snap', 'snap.manifest.json'),
JSON.stringify(
getSnapManifest({
shasum: 'G/W5b2JZVv+epgNX9pkN63X6Lye9EJVJ4NLSgAw/afd=',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ export async function manifest(
exports?: string[],
spinner?: Ora,
): Promise<boolean> {
const { reports, updated } = await checkManifest(dirname(path), {
/* eslint-disable no-console */
console.log('Checking manifest at path:', path);
console.log('Dirname:', dirname(path));
/* eslint-enable no-console */
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug console.log statements left in production code

Medium Severity

Debug console.log statements logging the manifest path and dirname were accidentally committed. These lines output to the console every time the manifest function is called, which will clutter the CLI output in production use. The explicit eslint-disable no-console comments suggest these were temporary debugging additions that weren't removed before committing.

Fix in Cursor Fix in Web


const { reports, updated } = await checkManifest(path, {
exports,
handlerEndowments,
updateAndWriteManifest: write,
Expand Down
17 changes: 16 additions & 1 deletion packages/snaps-cli/src/commands/watch/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import type { YargsArgs } from '../../types/yargs';

jest.mock('./watch');

const getMockArgv = () => {
const getMockArgv = (manifest?: string | undefined) => {
return {
context: { config: getMockConfig() },
manifest,
} as unknown as YargsArgs;
};

Expand All @@ -16,4 +17,18 @@ describe('watch command', () => {
await command.handler(getMockArgv());
expect(watchHandler).toHaveBeenCalled();
});

it('calls the `watchHandler` function with a custom manifest path', async () => {
await command.handler(getMockArgv('/custom.json'));
expect(watchHandler).toHaveBeenCalledWith(
expect.objectContaining({
manifest: expect.objectContaining({
path: expect.stringContaining('custom.json'),
}),
}),
{
port: undefined,
},
);
});
});
20 changes: 16 additions & 4 deletions packages/snaps-cli/src/commands/watch/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { resolve } from 'path';
import type yargs from 'yargs';

import { watchHandler } from './watch';
Expand All @@ -8,12 +9,23 @@ const command = {
command: ['watch', 'w'],
desc: 'Build Snap on change',
builder: (yarg: yargs.Argv) => {
yarg.option('port', builders.port);
yarg.option('port', builders.port).option('manifest', builders.manifest);
},
handler: async (argv: YargsArgs) =>
watchHandler(argv.context.config, {
handler: async (argv: YargsArgs) => {
const configWithManifest = argv.manifest
? {
...argv.context.config,
manifest: {
...argv.context.config.manifest,
path: resolve(process.cwd(), argv.manifest),
},
}
: argv.context.config;

return await watchHandler(configWithManifest, {
port: argv.port,
}),
});
},
};

export * from './implementation';
Expand Down
1 change: 1 addition & 0 deletions packages/snaps-cli/src/types/yargs.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type YargsArgs = {
dist: string;
src: string;
eval: boolean;
manifest?: string;
outfileName: string;
serve: boolean;
directory?: string;
Expand Down
46 changes: 43 additions & 3 deletions packages/snaps-cli/src/webpack/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ describe('getAllowedPaths', () => {

describe('getServer', () => {
beforeEach(async () => {
await fs.mkdir('/foo', { recursive: true });
await fs.mkdir('/foo/dist', { recursive: true });
await fs.writeFile('/foo/dist/bundle.js', 'console.log("Hello, world!");');
await fs.writeFile(
'/foo/snap.manifest.json',
JSON.stringify(getSnapManifest()),
Expand Down Expand Up @@ -207,17 +208,53 @@ describe('getServer', () => {
root: '/foo',
port: 0,
},
output: {
path: '/foo/dist',
},
manifest: {
path: '/foo/snap.manifest.json',
},
});

const server = getServer(config);
const { port, close } = await server.listen();

const response = await fetch(
`http://localhost:${port}/snap.manifest.json?_=1731493314736`,
`http://localhost:${port}/dist/bundle.js?_=1731493314736`,
);

expect(response.status).toBe(200);
expect(await response.text()).toBe('console.log("Hello, world!");');

await close();
});

it('responds with a custom manifest file', async () => {
const config = getMockConfig({
input: 'src/index.js',
server: {
root: '/foo',
port: 0,
},
manifest: {
path: '/foo/snap.manifest.dev.json',
},
});

const server = getServer(config);
const { port, close } = await server.listen();

// Create a custom manifest file in the /foo/dist directory
const customManifest = getSnapManifest({ proposedName: 'Dev Snap' });
await fs.writeFile(
'/foo/snap.manifest.dev.json',
JSON.stringify(customManifest),
);

const response = await fetch(`http://localhost:${port}/snap.manifest.json`);

expect(response.status).toBe(200);
expect(await response.text()).toBe(JSON.stringify(getSnapManifest()));
expect(await response.text()).toBe(JSON.stringify(customManifest));

await close();
});
Expand All @@ -229,6 +266,9 @@ describe('getServer', () => {
root: '/foo',
port: 0,
},
manifest: {
path: '/foo/snap.manifest.json',
},
});

const server = getServer(config);
Expand Down
30 changes: 27 additions & 3 deletions packages/snaps-cli/src/webpack/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { Express, Request } from 'express';
import express, { static as expressStatic } from 'express';
import type { Server } from 'http';
import type { AddressInfo } from 'net';
import { join, relative, resolve as resolvePath, sep, posix } from 'path';
import { relative, resolve as resolvePath, sep, posix } from 'path';

import type { ProcessedConfig } from '../config';

Expand Down Expand Up @@ -91,8 +91,10 @@ export function getAllowedPaths(
* `false` if it is not.
*/
async function isAllowedPath(request: Request, config: ProcessedConfig) {
const manifestPath = join(config.server.root, NpmSnapFileNames.Manifest);
const { result } = await readJsonFile<SnapManifest>(manifestPath);
const { result } = await readJsonFile<SnapManifest>(
resolvePath(config.server.root, config.manifest.path),
);

const allowedPaths = getAllowedPaths(config, result);

const path = request.path.slice(1);
Expand Down Expand Up @@ -140,6 +142,28 @@ export function getServer(
.catch(next);
});

// Serve the manifest file at the expected URL.
app.get('/snap.manifest.json', (_request, response, next) => {
response.sendFile(
resolvePath(config.server.root, config.manifest.path),
{
headers: {
'Cache-Control': 'no-cache',
'Access-Control-Allow-Origin': '*',
},
},

// This is complicated to test, since this middleware is only called if
// the file exists in the first place.
/* istanbul ignore next */
(error) => {
if (error) {
next(error);
}
},
);
});

// Serve the static files.
app.use(
expressStatic(config.server.root, {
Expand Down
4 changes: 2 additions & 2 deletions packages/snaps-rollup-plugin/src/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ describe('snaps', () => {
});

expect(mock).toHaveBeenCalledTimes(1);
expect(mock).toHaveBeenCalledWith('/', {
expect(mock).toHaveBeenCalledWith('/snap.manifest.json', {
updateAndWriteManifest: true,
sourceCode: expect.any(String),
});
Expand All @@ -260,7 +260,7 @@ describe('snaps', () => {
});

expect(mock).toHaveBeenCalledTimes(1);
expect(mock).toHaveBeenCalledWith('/', {
expect(mock).toHaveBeenCalledWith('/snap.manifest.json', {
updateAndWriteManifest: false,
sourceCode: expect.any(String),
});
Expand Down
11 changes: 4 additions & 7 deletions packages/snaps-rollup-plugin/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,10 @@ export default function snaps(options?: Partial<Options>): Plugin {
}

if (defaultOptions.manifestPath) {
const { reports } = await checkManifest(
pathUtils.dirname(defaultOptions.manifestPath),
{
updateAndWriteManifest: defaultOptions.writeManifest,
sourceCode: await fs.readFile(output.file, 'utf8'),
},
);
const { reports } = await checkManifest(defaultOptions.manifestPath, {
updateAndWriteManifest: defaultOptions.writeManifest,
sourceCode: await fs.readFile(output.file, 'utf8'),
});

const errorsUnfixed = reports
.filter((report) => report.severity === 'error' && !report.wasFixed)
Expand Down
Loading
Loading