Skip to content

Commit

Permalink
Format Snap manifests with Prettier (#2787)
Browse files Browse the repository at this point in the history
This updates the CLI to format Snap manifests with Prettier before
writing to disk.

Closes #786.
  • Loading branch information
Mrtenz authored Oct 4, 2024
1 parent b2448fb commit 82ee203
Show file tree
Hide file tree
Showing 15 changed files with 253 additions and 42 deletions.
2 changes: 0 additions & 2 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,3 @@ packages/examples/examples/webpack/index.html
packages/snaps-execution-environments/lavamoat/**/*.json
packages/snaps-jest/public
packages/snaps-simulator/vendor

packages/examples/packages/*/snap.manifest.json
24 changes: 4 additions & 20 deletions packages/examples/packages/bip32/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,37 +24,21 @@
"snap_dialog": {},
"snap_getBip32Entropy": [
{
"path": [
"m",
"44'",
"0'"
],
"path": ["m", "44'", "0'"],
"curve": "secp256k1"
},
{
"path": [
"m",
"44'",
"0'"
],
"path": ["m", "44'", "0'"],
"curve": "ed25519"
},
{
"path": [
"m",
"44'",
"0'"
],
"path": ["m", "44'", "0'"],
"curve": "ed25519Bip32"
}
],
"snap_getBip32PublicKey": [
{
"path": [
"m",
"44'",
"0'"
],
"path": ["m", "44'", "0'"],
"curve": "secp256k1"
}
]
Expand Down
4 changes: 1 addition & 3 deletions packages/examples/packages/bip44/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@
"initialPermissions": {
"endowment:rpc": {
"dapps": true,
"allowedOrigins": [
"npm:@metamask/json-rpc-example-snap"
]
"allowedOrigins": ["npm:@metamask/json-rpc-example-snap"]
},
"snap_dialog": {},
"snap_getBip44Entropy": [
Expand Down
4 changes: 1 addition & 3 deletions packages/examples/packages/get-file/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@
"registry": "https://registry.npmjs.org"
}
},
"files": [
"./files/foo.json"
]
"files": ["./files/foo.json"]
},
"initialPermissions": {
"endowment:rpc": {
Expand Down
6 changes: 1 addition & 5 deletions packages/examples/packages/localization/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,7 @@
"registry": "https://registry.npmjs.org/"
}
},
"locales": [
"locales/da.json",
"locales/en.json",
"locales/nl.json"
]
"locales": ["locales/da.json", "locales/en.json", "locales/nl.json"]
},
"initialPermissions": {
"endowment:rpc": {
Expand Down
4 changes: 1 addition & 3 deletions packages/examples/packages/name-lookup/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@
},
"initialPermissions": {
"endowment:name-lookup": {
"chains": [
"eip155:1"
]
"chains": ["eip155:1"]
}
},
"manifestVersion": "0.1"
Expand Down
59 changes: 59 additions & 0 deletions packages/snaps-cli/src/commands/manifest/implementation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,63 @@ describe('manifest', () => {
expect.stringMatching('The snap manifest file has been updated.'),
);
});

it('formats a snap manifest with Prettier', async () => {
const error = jest.spyOn(console, 'error').mockImplementation();
const log = jest.spyOn(console, 'log').mockImplementation();

await fs.writeFile(
'/snap/snap.manifest.json',
JSON.stringify(
getSnapManifest({
shasum: 'G/W5b2JZVv+epgNX9pkN63X6Lye9EJVJ4NLSgAw/afd=',
initialPermissions: {
'endowment:name-lookup': {
chains: ['eip155:1', 'eip155:2', 'eip155:3'],
},
},
}),
),
);

const spinner = ora();
const result = await manifest('/snap/snap.manifest.json', true, spinner);
expect(result).toBe(true);

expect(error).not.toHaveBeenCalled();
expect(log).toHaveBeenCalledWith(
expect.stringMatching('The snap manifest file has been updated.'),
);

expect(await fs.readFile('/snap/snap.manifest.json', 'utf8'))
.toMatchInlineSnapshot(`
"{
"version": "1.0.0",
"description": "The test example snap!",
"proposedName": "@metamask/example-snap",
"repository": {
"type": "git",
"url": "https://github.com/MetaMask/example-snap.git"
},
"source": {
"shasum": "d4W7f1lzpVGMj8jjCn1lYhhHmKc/9TSk5QLH5ldKQoI=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
"packageName": "@metamask/example-snap",
"registry": "https://registry.npmjs.org",
"iconPath": "images/icon.svg"
}
}
},
"initialPermissions": {
"endowment:name-lookup": {
"chains": ["eip155:1", "eip155:2", "eip155:3"]
}
},
"manifestVersion": "0.1"
}
"
`);
});
});
2 changes: 2 additions & 0 deletions packages/snaps-cli/src/commands/manifest/implementation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { checkManifest, indent } from '@metamask/snaps-utils/node';
import { writeManifest } from '@metamask/snaps-webpack-plugin';
import { assert } from '@metamask/utils';
import { red, yellow, green } from 'chalk';
import type { Ora } from 'ora';
Expand All @@ -24,6 +25,7 @@ export async function manifest(
): Promise<boolean> {
const { reports, updated } = await checkManifest(dirname(path), {
updateAndWriteManifest: write,
writeFileFn: writeManifest,
});

const errors = [];
Expand Down
2 changes: 1 addition & 1 deletion packages/snaps-webpack-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"@metamask/snaps-sdk": "workspace:^",
"@metamask/snaps-utils": "workspace:^",
"@metamask/utils": "^9.2.1",
"prettier": "^2.8.8",
"webpack-sources": "^3.2.3"
},
"devDependencies": {
Expand Down Expand Up @@ -90,7 +91,6 @@
"jest-it-up": "^2.0.0",
"jest-silent-reporter": "^0.6.0",
"memfs": "^3.4.13",
"prettier": "^2.8.8",
"prettier-plugin-packagejson": "^2.5.2",
"typescript": "~5.3.3",
"webpack": "^5.88.0"
Expand Down
4 changes: 4 additions & 0 deletions packages/snaps-webpack-plugin/src/__fixtures__/.prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Prettier config used for testing.
module.exports = {
tabWidth: 4,
};
1 change: 1 addition & 0 deletions packages/snaps-webpack-plugin/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { writeManifest } from './manifest';
export { default } from './plugin';
export type { Options } from './plugin';
121 changes: 121 additions & 0 deletions packages/snaps-webpack-plugin/src/manifest.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { getSnapManifest } from '@metamask/snaps-utils/test-utils';
import { promises as fs } from 'fs';
import { resolve } from 'path';

import { writeManifest } from './manifest';

jest.mock('fs', () => ({
...jest.requireActual('fs'),
promises: {
writeFile: jest.fn(),
},
}));

describe('writeManifest', () => {
it('formats the manifest with Prettier before writing to disk', async () => {
const manifest = JSON.stringify(getSnapManifest());
await writeManifest('test.json', manifest);

expect(jest.mocked(fs.writeFile).mock.calls[0][1]).toMatchInlineSnapshot(`
"{
"version": "1.0.0",
"description": "The test example snap!",
"proposedName": "@metamask/example-snap",
"repository": {
"type": "git",
"url": "https://github.com/MetaMask/example-snap.git"
},
"source": {
"shasum": "rNyfINgNh161cBmUop+F7xlE+GSEDZH53Y/HDpGLGGg=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
"packageName": "@metamask/example-snap",
"registry": "https://registry.npmjs.org",
"iconPath": "images/icon.svg"
}
}
},
"initialPermissions": {
"snap_dialog": {},
"endowment:rpc": { "snaps": true, "dapps": false }
},
"manifestVersion": "0.1"
}
"
`);
});

it('uses a custom Prettier config if found', async () => {
const manifest = JSON.stringify(getSnapManifest());
await writeManifest(
resolve(__dirname, '__fixtures__', 'foo.json'),
manifest,
);

expect(jest.mocked(fs.writeFile).mock.calls[0][1]).toMatchInlineSnapshot(`
"{
"version": "1.0.0",
"description": "The test example snap!",
"proposedName": "@metamask/example-snap",
"repository": {
"type": "git",
"url": "https://github.com/MetaMask/example-snap.git"
},
"source": {
"shasum": "rNyfINgNh161cBmUop+F7xlE+GSEDZH53Y/HDpGLGGg=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
"packageName": "@metamask/example-snap",
"registry": "https://registry.npmjs.org",
"iconPath": "images/icon.svg"
}
}
},
"initialPermissions": {
"snap_dialog": {},
"endowment:rpc": { "snaps": true, "dapps": false }
},
"manifestVersion": "0.1"
}
"
`);
});

it('accepts a custom write function', async () => {
const fn = jest.fn();
const manifest = JSON.stringify(getSnapManifest());
await writeManifest('test.json', manifest, fn);

expect(fn).toHaveBeenCalledTimes(1);
expect(fn.mock.calls[0][1]).toMatchInlineSnapshot(`
"{
"version": "1.0.0",
"description": "The test example snap!",
"proposedName": "@metamask/example-snap",
"repository": {
"type": "git",
"url": "https://github.com/MetaMask/example-snap.git"
},
"source": {
"shasum": "rNyfINgNh161cBmUop+F7xlE+GSEDZH53Y/HDpGLGGg=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
"packageName": "@metamask/example-snap",
"registry": "https://registry.npmjs.org",
"iconPath": "images/icon.svg"
}
}
},
"initialPermissions": {
"snap_dialog": {},
"endowment:rpc": { "snaps": true, "dapps": false }
},
"manifestVersion": "0.1"
}
"
`);
});
});
32 changes: 32 additions & 0 deletions packages/snaps-webpack-plugin/src/manifest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { WriteFileFunction } from '@metamask/snaps-utils/node';
import { promises as fs } from 'fs';
import { format, resolveConfig } from 'prettier';

/**
* Format the manifest data with Prettier and write it to disk.
*
* It uses the Prettier configuration found in the project directory (if any),
* or the default Prettier configuration if none is found.
*
* @param path - The path to write the manifest to.
* @param data - The manifest data.
* @param writeFileFn - The function to use to write the manifest.
* @returns A promise that resolves when the manifest has been written.
*/
export async function writeManifest(
path: string,
data: string,
writeFileFn: WriteFileFunction = fs.writeFile,
) {
const config = await resolveConfig(path, {
editorconfig: true,
});

const formattedManifest = format(data, {
...config,
parser: 'json',
filepath: path,
});

await writeFileFn(path, formattedManifest);
}
16 changes: 16 additions & 0 deletions packages/snaps-webpack-plugin/src/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import * as pathUtils from 'path';
import type { Stats, Configuration } from 'webpack';
import webpack from 'webpack';

import { writeManifest } from './manifest';
import type { Options } from './plugin';
import SnapsWebpackPlugin from './plugin';

Expand All @@ -24,6 +25,10 @@ jest.mock('@metamask/snaps-utils/node', () => ({
checkManifest: jest.fn(),
}));

jest.mock('./manifest', () => ({
writeManifest: jest.fn(),
}));

type BundleOptions = {
code?: string;
options?: Options;
Expand Down Expand Up @@ -215,6 +220,17 @@ describe('SnapsWebpackPlugin', () => {
sourceCode: expect.any(String),
writeFileFn: expect.any(Function),
});

const writeFileFn = mock.mock.calls[0][1]?.writeFileFn;
expect(writeFileFn).toBeDefined();
await writeFileFn?.('/snap.manifest.json', 'foo');

expect(writeManifest).toHaveBeenCalledTimes(1);
expect(writeManifest).toHaveBeenCalledWith(
'/snap.manifest.json',
'foo',
expect.any(Function),
);
});

it('does not fix the manifest if configured', async () => {
Expand Down
Loading

0 comments on commit 82ee203

Please sign in to comment.