diff --git a/AGENTS.md b/AGENTS.md index d3693e6e1..5e0165a00 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -29,7 +29,7 @@ Package in this monorepo are represented by subdirectories in `packages/`. Each - `LICENSE` — Each package has a license that describes how engineers can use it in projects. - Configuration files — See below. -Note that the package template in `scripts/create-package/package-template` also uses this same structure. +Note that the package template in `scripts/create-package/library-template` also uses this same structure. ### Configuration files @@ -55,7 +55,7 @@ The monorepo uses a hierarchical configuration approach for different tools. For - `tsconfig.scripts.json` defines shared TypeScript settings for directories in `scripts/`. - The root `lint:tsc` script checks repository scripts with `tsconfig.json`, then checks each workspace package configuration directly. It does not build Snap bundles with `tsc`. -- `packages/**/tsconfig.json` (and `scripts/create-package/package-template/tsconfig.json`) defines TypeScript settings for each package that are meant to be used by code editors and type checking. +- `packages/**/tsconfig.json` (and `scripts/create-package/library-template/tsconfig.json`) defines TypeScript settings for each package that are meant to be used by code editors and type checking. - Non-Snap packages and the package template also have `tsconfig.build.json` files for `ts-bridge` declaration builds. Snap packages do not have build configs because `mm-snap` builds their bundles. - `scripts/create-package/tsconfig.json` customizes TypeScript settings for the `create-package` tool. @@ -63,7 +63,7 @@ The monorepo uses a hierarchical configuration approach for different tools. For - `jest.config.packages.js` defines shared Jest settings for all directories in `packages/`. - `jest.config.scripts.js` defines shared Jest settings for all directories in `scripts/`. -- `packages/**/jest.config.js` (and `scripts/create-package/package-template/jest.config.js`) customizes Jest settings for each package. +- `packages/**/jest.config.js` (and `scripts/create-package/library-template/jest.config.js`) customizes Jest settings for each package. #### ESLint @@ -76,7 +76,7 @@ The monorepo uses a hierarchical configuration approach for different tools. For #### TypeDoc -- `packages/**/typedoc.json` (and `scripts/create-package/package-template/typedoc.js`) defines TypeDoc settings for each package. +- `packages/**/typedoc.json` (and `scripts/create-package/library-template/typedoc.json`) defines TypeDoc settings for each package. #### Other files @@ -169,7 +169,7 @@ Each consumer-facing change to a package should be accompanied by one or more en ## Adding new packages -Use `yarn create-package --name --description ` to add a new package to the monorepo. +Use `yarn create-package --type --name --description ` to add a new package to the monorepo. The `--type` option is required: use `snap` to scaffold a new Snap, or `library` (shorthand: `lib`) to scaffold a new non-Snap package. ## Code guidelines @@ -225,12 +225,12 @@ Use the `sample-gas-prices-service/` directory in the `sample-controllers` packa ## Cursor Cloud specific instructions -This repo is a Yarn 4 monorepo of MetaMask Snaps. The two products live in `packages/`: `@metamask/bitcoin-wallet-snap` and `@metamask/sample-snap`. Dependencies are already installed by the startup update script (`corepack enable` + `yarn install`), so no install step is needed at session start. Yarn is pinned via `packageManager` (`yarn@4.17.1`) and provisioned by corepack; use `yarn ...`, not the classic global yarn. +This repo is a Yarn 4 monorepo of MetaMask Snaps. The products live in `packages/`, including `@metamask/bitcoin-wallet-snap`. Dependencies are already installed by the startup update script (`corepack enable` + `yarn install`), so no install step is needed at session start. Yarn is pinned via `packageManager` (`yarn@4.17.1`) and provisioned by corepack; use `yarn ...`, not the classic global yarn. Standard commands are documented above (see "Running tests", "Linting and formatting", "Building packages"). Non-obvious caveats for running things here: - **Build before testing.** A Snap's Jest suite (`@metamask/snaps-jest`) expects the built bundle. CI always runs `yarn workspace build` before `yarn workspace run test`. If tests behave unexpectedly, run `yarn build` (or the per-package build) first. - **`yarn lint` deletes `dist/`.** `lint:eslint` runs `build:only-clean` (`rimraf -g 'packages/*/dist'`) before linting. After running `yarn lint`, re-run `yarn build` before serving a snap or running snap tests. - **Running a snap:** `yarn workspace run serve` serves the pre-built bundle at `http://localhost:8080` (`/snap.manifest.json` and `/dist/bundle.js`); `yarn workspace run start` (`mm-snap watch`) rebuilds on change. Both snaps use port 8080, so only run one at a time. -- **No headless end-to-end.** Fully exercising a snap normally requires the MetaMask extension in a browser, which isn't available headless. Use the `snaps-jest` test suites (they install the snap and invoke its JSON-RPC methods, e.g. sample-snap's `hello`) to exercise core functionality without a browser. +- **No headless end-to-end.** Fully exercising a snap normally requires the MetaMask extension in a browser, which isn't available headless. Use the `snaps-jest` test suites (they install the snap and invoke its JSON-RPC methods, e.g. the generated snap's `hello`) to exercise core functionality without a browser. - **`.env` is optional** for `bitcoin-wallet-snap`; `snap.config.ts` reads it via dotenv but all values have sane defaults (see `.env.example`), so the snap builds and serves without one. diff --git a/docs/processes/adding-new-packages.md b/docs/processes/adding-new-packages.md index 45bec7fc5..7bea3581e 100644 --- a/docs/processes/adding-new-packages.md +++ b/docs/processes/adding-new-packages.md @@ -6,6 +6,9 @@ Manually creating a new monorepo package can be a tedious, even frustrating process. To alleviate that problem, we have created a CLI that automates most of the job for us, creatively titled [`create-package`](../../scripts/create-package/). To create a new monorepo package, follow these steps: 1. Create a new package using `yarn create-package`. + - You must specify the kind of package you want with the `--type` option. There is no default: + - `--type snap` creates a new Snap, whose bundle is built with `mm-snap`. + - `--type library` (or the `--type lib` shorthand) creates a new non-Snap package, whose declarations are built with `ts-bridge`. - Use the `--help` flag for usage information. - Once this is done, you can find a package with your chosen name in `/packages`. 2. Make sure your license is correct. @@ -14,7 +17,7 @@ Manually creating a new monorepo package can be a tedious, even frustrating proc 3. Update `.github/CODEOWNERS` to assign a team as the owner of the new package. 4. Add your dependencies. - Do this as normal using `yarn`. - - Remember, if you are adding other monorepo packages as dependents, don't forget to add them to the `references` array in your package's `tsconfig.json` and `tsconfig.build.json`. + - Remember, if you are adding other monorepo packages as dependents, don't forget to add them to the `references` array in your package's `tsconfig.json` and, for non-Snap packages, `tsconfig.build.json`. And that's it! @@ -22,8 +25,8 @@ And that's it! Along with this documentation, `create-package` is intended to be the source of truth for the process of adding new packages to the monorepo. Consequently, to change that process, you will want to change `create-package`. -The `create-package` directory contains a [template package](../../scripts/create-package/package-template/). The CLI is not aware of the contents of the template, only that its files have [placeholder values](../../scripts/create-package/constants.ts). When a new package is created, the template files are read from disk, the placeholder values are replaced with real ones, and the updated files are added to a new directory in `/packages`. To modify the template package: +The `create-package` directory contains a template for each package type: a [template Snap](../../scripts/create-package/snap-template/) and a [template library](../../scripts/create-package/library-template/). The CLI is not aware of the contents of the templates, only that their files have [placeholder values](../../scripts/create-package/constants.ts). When a new package is created, the template files for the requested type are read from disk, the placeholder values are replaced with real ones, and the updated files are added to a new directory in `/packages`. To modify the templates: -- If you need to add or modify any files or folders, just go ahead and make your changes in [`/scripts/create-package/package-template`](../../scripts/create-package/package-template/). The CLI will read whatever's in that directory and write it to disk. +- If you need to add or modify any files or folders, just go ahead and make your changes in [`/scripts/create-package/snap-template`](../../scripts/create-package/snap-template/) or [`/scripts/create-package/library-template`](../../scripts/create-package/library-template/). The CLI will read whatever's in those directories and write it to disk. - If you need to add or modify any placeholders, make sure that your desired values are added to both the relevant file(s) and [`/scripts/create-package/constants.ts`](../../scripts/create-package/constants.ts). Then, update the implementation of the CLI accordingly. - As with placeholders, updating the monorepo files that the CLI interacts with begins by updating [`/scripts/create-package/constants.ts`](../../scripts/create-package/constants.ts). diff --git a/eslint.config.mjs b/eslint.config.mjs index 81f39389a..5b0b91a48 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -77,7 +77,8 @@ const config = createConfig([ '**/dist/**', '**/docs/**', '.yarn/**', - 'scripts/create-package/package-template/**', + 'scripts/create-package/snap-template/**', + 'scripts/create-package/library-template/**', ], }, { diff --git a/jest.config.scripts.js b/jest.config.scripts.js index 517b1dded..d6882d4fd 100644 --- a/jest.config.scripts.js +++ b/jest.config.scripts.js @@ -14,7 +14,7 @@ module.exports = { coverageDirectory: '/scripts/coverage', // An array of regexp pattern strings used to skip coverage collection - coveragePathIgnorePatterns: ['/package-template/'], + coveragePathIgnorePatterns: ['/(snap|library)-template/'], // Indicates which provider should be used to instrument code for coverage coverageProvider: 'babel', @@ -65,7 +65,7 @@ module.exports = { ], // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped - testPathIgnorePatterns: ['/package-template/'], + testPathIgnorePatterns: ['/(snap|library)-template/'], // Default timeout of a test in milliseconds. testTimeout: 5000, diff --git a/package.json b/package.json index 89f67c73f..d11c76d81 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "test:clean": "yarn workspaces foreach --all --parallel --verbose run test:clean && yarn test", "test:packages": "yarn test:verbose --silent --collectCoverage=false --reporters=jest-silent-reporter", "test:scripts": "NODE_OPTIONS=--experimental-vm-modules yarn jest --config ./jest.config.scripts.js --silent", - "test:verbose": "yarn workspaces foreach --all --exclude @metamask/sample-snap --parallel --verbose run test:verbose", + "test:verbose": "yarn workspaces foreach --all --parallel --verbose run test:verbose", "workspaces:list-versions": "./scripts/list-workspace-versions.sh" }, "devDependencies": { @@ -53,7 +53,6 @@ "@typescript-eslint/eslint-plugin": "^8.48.0", "@typescript-eslint/parser": "^8.48.0", "@yarnpkg/types": "^4.0.0", - "comment-json": "^4.5.1", "depcheck": "^1.4.7", "eslint": "^9.39.1", "eslint-config-prettier": "^9.1.0", @@ -72,7 +71,6 @@ "lodash": "^4.17.21", "nock": "^13.3.1", "oxfmt": "^0.45.0", - "prettier": "^3.3.3", "prettier-2": "npm:prettier@^2.8.8", "rimraf": "^6.0.1", "semver": "^7.6.3", diff --git a/packages/sample-snap/CHANGELOG.md b/packages/sample-snap/CHANGELOG.md deleted file mode 100644 index e1c218cee..000000000 --- a/packages/sample-snap/CHANGELOG.md +++ /dev/null @@ -1,19 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -### Added - -- Add package to `internal-snaps` monorepo ([#8](https://github.com/MetaMask/internal-snaps/pull/8)) - -### Changed - -- Bump `@metamask/snaps-cli` from `^8.3.0` to `^8.4.1` ([#56](https://github.com/MetaMask/internal-snaps/pull/56)) -- Bump `@metamask/snaps-sdk` from `^11.1.1` to `^12.0.1` ([#56](https://github.com/MetaMask/internal-snaps/pull/56), [#214](https://github.com/MetaMask/internal-snaps/pull/214)) - -[Unreleased]: https://github.com/MetaMask/internal-snaps/ diff --git a/packages/sample-snap/README.md b/packages/sample-snap/README.md deleted file mode 100644 index c3601a1f1..000000000 --- a/packages/sample-snap/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# TypeScript Example Snap - -This snap demonstrates how to develop a snap with TypeScript. It is a simple -snap that displays a confirmation dialog when the `hello` JSON-RPC method is -called. - -## Testing - -The snap comes with some basic tests, to demonstrate how to write tests for -snaps. To test the snap, run `yarn test` in this directory. This will use -[`@metamask/snaps-jest`](https://github.com/MetaMask/snaps/tree/main/packages/snaps-jest) -to run the tests in `src/index.test.ts`. diff --git a/scripts/create-package/cli.test.ts b/scripts/create-package/cli.test.ts index c95ed759a..b0cdee86a 100644 --- a/scripts/create-package/cli.test.ts +++ b/scripts/create-package/cli.test.ts @@ -16,27 +16,31 @@ function getMockArgv(...args: string[]): string[] { } /** - * Returns the parsed `yargs.Arguments` object for a given package name and - * description. + * Returns the parsed `yargs.Arguments` object for a given package name, + * description, and type. * * @param name - The package name. * @param description - The package description. + * @param type - The package type. * @returns The parsed argv object. */ function getParsedArgv( name: string, description: string, + type: string, ): { _: []; $0: 'create-package'; name: `@metamask/${string}`; description: string; + type: string; } { return { _: [], $0: 'create-package', name: `@metamask/${name}`, description, + type, }; } @@ -67,9 +71,9 @@ describe('create-package/cli', () => { const defaultCommand = commandMap.$0; jest.spyOn(defaultCommand, 'handler').mockImplementation(); - await expect(cli(getMockArgv('--name', ' '), commands)).rejects.toThrow( - 'exit: 1', - ); + await expect( + cli(getMockArgv('--name', ' ', '--type', 'snap'), commands), + ).rejects.toThrow('exit: 1'); expect(console.error).toHaveBeenCalledWith( 'The argument "name" was processed to an empty string. Please provide a value with non-whitespace characters.', @@ -82,24 +86,20 @@ describe('create-package/cli', () => { jest.spyOn(defaultCommand, 'handler'); jest.spyOn(utils, 'readMonorepoFiles').mockResolvedValue({ - tsConfig: {}, - tsConfigBuild: {}, nodeVersions: '>=18.0.0', - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any); + }); jest.spyOn(utils, 'finalizeAndWriteData').mockResolvedValue(); expect( await cli( - getMockArgv('--name', 'foo', '--description', 'bar'), + getMockArgv('--name', 'foo', '--description', 'bar', '--type', 'lib'), commands, ), ).toBeUndefined(); expect(defaultCommand.handler).toHaveBeenCalledTimes(1); expect(defaultCommand.handler).toHaveBeenCalledWith( - expect.objectContaining(getParsedArgv('foo', 'bar')), + expect.objectContaining(getParsedArgv('foo', 'bar', 'library')), ); }); @@ -108,24 +108,44 @@ describe('create-package/cli', () => { jest.spyOn(defaultCommand, 'handler'); jest.spyOn(utils, 'readMonorepoFiles').mockResolvedValue({ - tsConfig: {}, - tsConfigBuild: {}, nodeVersions: '>=18.0.0', - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any); + }); jest.spyOn(utils, 'finalizeAndWriteData').mockResolvedValue(); expect( await cli( - getMockArgv('--name', '@metamask/foo', '--description', 'bar'), + getMockArgv( + '--name', + '@metamask/foo', + '--description', + 'bar', + '--type', + 'snap', + ), commands, ), ).toBeUndefined(); expect(defaultCommand.handler).toHaveBeenCalledTimes(1); expect(defaultCommand.handler).toHaveBeenCalledWith( - expect.objectContaining(getParsedArgv('foo', 'bar')), + expect.objectContaining(getParsedArgv('foo', 'bar', 'snap')), + ); + }); + + it('should normalize the "lib" shorthand to "library"', async () => { + const defaultCommand = commandMap.$0; + jest.spyOn(defaultCommand, 'handler').mockImplementation(); + + expect( + await cli( + getMockArgv('--name', 'foo', '--description', 'bar', '--type', 'lib'), + commands, + ), + ).toBeUndefined(); + + expect(defaultCommand.handler).toHaveBeenCalledTimes(1); + expect(defaultCommand.handler).toHaveBeenCalledWith( + expect.objectContaining(getParsedArgv('foo', 'bar', 'library')), ); }); @@ -135,14 +155,21 @@ describe('create-package/cli', () => { expect( await cli( - getMockArgv('--name', 'foo', '--description', 'bar'), + getMockArgv( + '--name', + 'foo', + '--description', + 'bar', + '--type', + 'library', + ), commands, ), ).toBeUndefined(); expect(defaultCommand.handler).toHaveBeenCalledTimes(1); expect(defaultCommand.handler).toHaveBeenCalledWith( - expect.objectContaining(getParsedArgv('foo', 'bar')), + expect.objectContaining(getParsedArgv('foo', 'bar', 'library')), ); }); @@ -151,7 +178,7 @@ describe('create-package/cli', () => { jest.spyOn(defaultCommand, 'handler').mockImplementation(); await expect( - cli(getMockArgv('--description', 'bar'), commands), + cli(getMockArgv('--description', 'bar', '--type', 'snap'), commands), ).rejects.toThrow('exit: 1'); expect(console.error).toHaveBeenCalledWith( @@ -163,13 +190,42 @@ describe('create-package/cli', () => { const defaultCommand = commandMap.$0; jest.spyOn(defaultCommand, 'handler').mockImplementation(); - await expect(cli(getMockArgv('--name', 'foo'), commands)).rejects.toThrow( - 'exit: 1', - ); + await expect( + cli(getMockArgv('--name', 'foo', '--type', 'snap'), commands), + ).rejects.toThrow('exit: 1'); expect(console.error).toHaveBeenCalledWith( 'Missing required argument: "description"', ); }); + + it('should error if the package type is missing', async () => { + const defaultCommand = commandMap.$0; + jest.spyOn(defaultCommand, 'handler').mockImplementation(); + + await expect( + cli(getMockArgv('--name', 'foo', '--description', 'bar'), commands), + ).rejects.toThrow('exit: 1'); + + expect(console.error).toHaveBeenCalledWith( + 'Missing required argument: "type". Please specify either "snap" or "library".', + ); + }); + + it('should error if the package type is invalid', async () => { + const defaultCommand = commandMap.$0; + jest.spyOn(defaultCommand, 'handler').mockImplementation(); + + await expect( + cli( + getMockArgv('--name', 'foo', '--description', 'bar', '--type', 'foo'), + commands, + ), + ).rejects.toThrow('exit: 1'); + + expect(console.error).toHaveBeenCalledWith( + 'Invalid package type: "foo". Valid types are: "snap", "library", "lib".', + ); + }); }); }); diff --git a/scripts/create-package/commands.test.ts b/scripts/create-package/commands.test.ts index cb6889d83..ac2f37ac1 100644 --- a/scripts/create-package/commands.test.ts +++ b/scripts/create-package/commands.test.ts @@ -2,6 +2,7 @@ import type { Arguments } from 'yargs'; import type { CreatePackageOptions } from './commands'; import { createPackageHandler } from './commands'; +import { PackageTypes } from './constants'; import * as utils from './utils'; jest.mock('./utils', () => ({ @@ -14,14 +15,8 @@ jest.useFakeTimers().setSystemTime(new Date('2023-01-02')); describe('create-package/commands', () => { describe('createPackageHandler', () => { - it('should create the expected package', async () => { + it('should create the expected library package', async () => { (utils.readMonorepoFiles as jest.Mock).mockResolvedValue({ - tsConfig: { - references: [{ path: '../packages/foo' }], - }, - tsConfigBuild: { - references: [{ path: '../packages/foo' }], - }, nodeVersions: '>=18.0.0', }); @@ -30,6 +25,7 @@ describe('create-package/commands', () => { $0: 'create-package', name: '@metamask/new-package', description: 'A new MetaMask package.', + type: PackageTypes.Library, }; await createPackageHandler(args); @@ -39,17 +35,43 @@ describe('create-package/commands', () => { { name: '@metamask/new-package', description: 'A new MetaMask package.', + type: PackageTypes.Library, directoryName: 'new-package', nodeVersions: '>=18.0.0', currentYear: '2023', }, { - tsConfig: { - references: [{ path: '../packages/foo' }], - }, - tsConfigBuild: { - references: [{ path: '../packages/foo' }], - }, + nodeVersions: '>=18.0.0', + }, + ); + }); + + it('should create the expected snap package', async () => { + (utils.readMonorepoFiles as jest.Mock).mockResolvedValue({ + nodeVersions: '>=18.0.0', + }); + + const args: Arguments = { + _: [], + $0: 'create-package', + name: '@metamask/new-snap', + description: 'A new MetaMask snap.', + type: PackageTypes.Snap, + }; + + await createPackageHandler(args); + + expect(utils.finalizeAndWriteData).toHaveBeenCalledTimes(1); + expect(utils.finalizeAndWriteData).toHaveBeenCalledWith( + { + name: '@metamask/new-snap', + description: 'A new MetaMask snap.', + type: PackageTypes.Snap, + directoryName: 'new-snap', + nodeVersions: '>=18.0.0', + currentYear: '2023', + }, + { nodeVersions: '>=18.0.0', }, ); diff --git a/scripts/create-package/commands.ts b/scripts/create-package/commands.ts index 662d369ca..b80c6c16b 100644 --- a/scripts/create-package/commands.ts +++ b/scripts/create-package/commands.ts @@ -4,12 +4,15 @@ import type { Arguments, } from 'yargs'; +import type { PackageType } from './constants'; +import { PackageTypeChoices, PackageTypes } from './constants'; import type { PackageData } from './utils'; import { finalizeAndWriteData, readMonorepoFiles } from './utils'; export type CreatePackageOptions = { name: string; description: string; + type: PackageType; }; export type CommandModule = YargsCommandModule & { @@ -40,10 +43,22 @@ const defaultCommand: CommandModule = { type: 'string', requiresArg: true, }, + + type: { + alias: 't', + describe: + 'The type of package to create, either "snap" or "library". "lib" is a shorthand for "library".', + type: 'string', + requiresArg: true, + }, }) .example( - '$0 --name fabulous-package --description "A fabulous package."', - 'Create a new package with the given name and description.', + '$0 --type snap --name fabulous-snap --description "A fabulous snap."', + 'Create a new Snap package with the given name and description.', + ) + .example( + '$0 -t lib -n fabulous-package -d "A fabulous package."', + 'Create a new library package using shorthand options.', ) .check((args) => { if (!args.name || typeof args.name !== 'string') { @@ -52,11 +67,31 @@ const defaultCommand: CommandModule = { if (!args.description || typeof args.description !== 'string') { throw new Error('Missing required argument: "description"'); } + if (!args.type || typeof args.type !== 'string') { + throw new Error( + 'Missing required argument: "type". Please specify either "snap" or "library".', + ); + } + + if (!PackageTypeChoices.includes(args.type as PackageType | 'lib')) { + throw new Error( + `Invalid package type: "${args.type}". Valid types are: ${[ + ...PackageTypeChoices, + ] + .map((type) => `"${type}"`) + .join(', ')}.`, + ); + } if (!args.name.startsWith('@metamask/')) { args.name = `@metamask/${args.name}`; } + // Normalize the "lib" shorthand to "library". + if (args.type === 'lib') { + args.type = PackageTypes.Library; + } + return true; }); @@ -85,6 +120,7 @@ export async function createPackageHandler( const packageData: PackageData = { name: args.name, description: args.description, + type: args.type, directoryName: args.name.slice('@metamask/'.length), nodeVersions: monorepoFileData.nodeVersions, currentYear: new Date().getFullYear().toString(), diff --git a/scripts/create-package/constants.ts b/scripts/create-package/constants.ts index dcf79cfc0..76648cfc0 100644 --- a/scripts/create-package/constants.ts +++ b/scripts/create-package/constants.ts @@ -3,12 +3,37 @@ */ export const MonorepoFiles = { PackageJson: 'package.json', - TsConfig: 'tsconfig.json', - TsConfigBuild: 'tsconfig.build.json', } as const; export type MonorepoFiles = (typeof MonorepoFiles)[keyof typeof MonorepoFiles]; +/** + * The types of packages that can be created. + */ +export const PackageTypes = { + Snap: 'snap', + Library: 'library', +} as const; + +export type PackageType = (typeof PackageTypes)[keyof typeof PackageTypes]; + +/** + * The accepted values for the `--type` option, including shorthands. + */ +export const PackageTypeChoices = [ + PackageTypes.Snap, + PackageTypes.Library, + 'lib', +] as const; + +/** + * The directories containing the templates for each package type. + */ +export const TemplateDirectories: Record = { + [PackageTypes.Snap]: 'snap-template', + [PackageTypes.Library]: 'library-template', +}; + /** * Placeholder values in package template files that need to be replaced with * actual values corresponding to the new package. diff --git a/packages/sample-snap/LICENSE b/scripts/create-package/library-template/LICENSE similarity index 100% rename from packages/sample-snap/LICENSE rename to scripts/create-package/library-template/LICENSE diff --git a/scripts/create-package/package-template/LICENSE.APACHE2 b/scripts/create-package/library-template/LICENSE.APACHE2 similarity index 100% rename from scripts/create-package/package-template/LICENSE.APACHE2 rename to scripts/create-package/library-template/LICENSE.APACHE2 diff --git a/scripts/create-package/package-template/LICENSE.MIT b/scripts/create-package/library-template/LICENSE.MIT similarity index 100% rename from scripts/create-package/package-template/LICENSE.MIT rename to scripts/create-package/library-template/LICENSE.MIT diff --git a/scripts/create-package/package-template/README.md b/scripts/create-package/library-template/README.md similarity index 100% rename from scripts/create-package/package-template/README.md rename to scripts/create-package/library-template/README.md diff --git a/scripts/create-package/package-template/jest.config.js b/scripts/create-package/library-template/jest.config.js similarity index 84% rename from scripts/create-package/package-template/jest.config.js rename to scripts/create-package/library-template/jest.config.js index ca0841333..f198bc8f9 100644 --- a/scripts/create-package/package-template/jest.config.js +++ b/scripts/create-package/library-template/jest.config.js @@ -14,6 +14,11 @@ module.exports = merge(baseConfig, { // The display name when running multiple projects displayName, + coveragePathIgnorePatterns: [ + ...(baseConfig.coveragePathIgnorePatterns ?? []), + '.*/__mocks__/', + ], + // An object that configures minimum threshold enforcement for coverage results coverageThreshold: { global: { diff --git a/scripts/create-package/package-template/package.json b/scripts/create-package/library-template/package.json similarity index 94% rename from scripts/create-package/package-template/package.json rename to scripts/create-package/library-template/package.json index 3e00c3fea..6abb84647 100644 --- a/scripts/create-package/package-template/package.json +++ b/scripts/create-package/library-template/package.json @@ -53,14 +53,14 @@ "devDependencies": { "@metamask/auto-changelog": "^6.1.1", "@ts-bridge/cli": "^0.6.4", - "@types/jest": "^29.5.14", + "@types/jest": "^30.0.0", "deepmerge": "^4.2.2", - "jest": "^29.7.0", - "ts-jest": "^29.2.5", + "jest": "30.0.3", + "ts-jest": "^29.4.1", "tsx": "^4.20.5", "typedoc": "^0.25.13", "typedoc-plugin-missing-exports": "^2.0.0", - "typescript": "~5.3.3" + "typescript": "~5.8.3" }, "engines": { "node": "NODE_VERSIONS" diff --git a/scripts/create-package/package-template/src/index.test.ts b/scripts/create-package/library-template/src/index.test.ts similarity index 100% rename from scripts/create-package/package-template/src/index.test.ts rename to scripts/create-package/library-template/src/index.test.ts diff --git a/scripts/create-package/package-template/src/index.ts b/scripts/create-package/library-template/src/index.ts similarity index 100% rename from scripts/create-package/package-template/src/index.ts rename to scripts/create-package/library-template/src/index.ts diff --git a/scripts/create-package/library-template/tsconfig.build.json b/scripts/create-package/library-template/tsconfig.build.json new file mode 100644 index 000000000..6cc6351fa --- /dev/null +++ b/scripts/create-package/library-template/tsconfig.build.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.packages.non-snaps.build.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "./dist", + "rootDir": "./src" + }, + /** + * Unlike Snap's which include all TypeScript, TSX and JSON files, here we want + * to mirror what gets built. + */ + "include": ["./src"] +} diff --git a/scripts/create-package/library-template/tsconfig.json b/scripts/create-package/library-template/tsconfig.json new file mode 100644 index 000000000..bc7c31680 --- /dev/null +++ b/scripts/create-package/library-template/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.packages.json", + "compilerOptions": { + "baseUrl": "./" + }, + /** + * Unlike Snap's which include all TypeScript, TSX and JSON files, here we want + * to mirror what gets built. + */ + "include": ["./src"] +} diff --git a/scripts/create-package/package-template/typedoc.json b/scripts/create-package/library-template/typedoc.json similarity index 100% rename from scripts/create-package/package-template/typedoc.json rename to scripts/create-package/library-template/typedoc.json diff --git a/scripts/create-package/package-template/tsconfig.build.json b/scripts/create-package/package-template/tsconfig.build.json deleted file mode 100644 index 7d5f92e04..000000000 --- a/scripts/create-package/package-template/tsconfig.build.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../../tsconfig.packages.non-snaps.build.json", - "compilerOptions": { - "baseUrl": "./", - "outDir": "./dist", - "rootDir": "./src" - }, - "include": ["./src"] -} diff --git a/scripts/create-package/package-template/tsconfig.json b/scripts/create-package/package-template/tsconfig.json deleted file mode 100644 index e0810056e..000000000 --- a/scripts/create-package/package-template/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../../tsconfig.packages.json", - "compilerOptions": { - "baseUrl": "./" - }, - "include": ["./src"] -} diff --git a/scripts/create-package/snap-template/CHANGELOG.md b/scripts/create-package/snap-template/CHANGELOG.md new file mode 100644 index 000000000..92e90f874 --- /dev/null +++ b/scripts/create-package/snap-template/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +[Unreleased]: https://github.com/MetaMask/internal-snaps/ diff --git a/scripts/create-package/package-template/LICENSE b/scripts/create-package/snap-template/LICENSE similarity index 100% rename from scripts/create-package/package-template/LICENSE rename to scripts/create-package/snap-template/LICENSE diff --git a/packages/sample-snap/LICENSE.APACHE2 b/scripts/create-package/snap-template/LICENSE.APACHE2 similarity index 99% rename from packages/sample-snap/LICENSE.APACHE2 rename to scripts/create-package/snap-template/LICENSE.APACHE2 index 18002eac9..c767c98de 100644 --- a/packages/sample-snap/LICENSE.APACHE2 +++ b/scripts/create-package/snap-template/LICENSE.APACHE2 @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2026 MetaMask + Copyright CURRENT_YEAR MetaMask Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/sample-snap/LICENSE.MIT b/scripts/create-package/snap-template/LICENSE.MIT similarity index 96% rename from packages/sample-snap/LICENSE.MIT rename to scripts/create-package/snap-template/LICENSE.MIT index e02786434..a867a4d54 100644 --- a/packages/sample-snap/LICENSE.MIT +++ b/scripts/create-package/snap-template/LICENSE.MIT @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2026 MetaMask +Copyright (c) CURRENT_YEAR MetaMask Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/scripts/create-package/snap-template/README.md b/scripts/create-package/snap-template/README.md new file mode 100644 index 000000000..85eb18197 --- /dev/null +++ b/scripts/create-package/snap-template/README.md @@ -0,0 +1,17 @@ +# `PACKAGE_NAME` + +PACKAGE_DESCRIPTION + +This snap displays a confirmation dialog when the `hello` JSON-RPC method is +called. + +## Testing + +The snap comes with some basic tests, to demonstrate how to write tests for +snaps. To test the snap, run `yarn test` in this directory. This will use +[`@metamask/snaps-jest`](https://github.com/MetaMask/snaps/tree/main/packages/snaps-jest) +to run the tests in `src/index.test.tsx`. + +## Contributing + +This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/internal-snaps#readme). diff --git a/packages/sample-snap/jest.config.js b/scripts/create-package/snap-template/jest.config.js similarity index 100% rename from packages/sample-snap/jest.config.js rename to scripts/create-package/snap-template/jest.config.js diff --git a/packages/sample-snap/package.json b/scripts/create-package/snap-template/package.json similarity index 78% rename from packages/sample-snap/package.json rename to scripts/create-package/snap-template/package.json index 868d93eb7..d82e264b2 100644 --- a/packages/sample-snap/package.json +++ b/scripts/create-package/snap-template/package.json @@ -1,13 +1,13 @@ { - "name": "@metamask/sample-snap", + "name": "PACKAGE_NAME", "version": "0.0.0", "private": true, - "description": "The 'Hello, world!' of MetaMask Snaps, now written in TypeScript", + "description": "PACKAGE_DESCRIPTION", "keywords": [ "Ethereum", "MetaMask" ], - "homepage": "https://github.com/MetaMask/internal-snaps/tree/main/packages/sample-snap#readme", + "homepage": "https://github.com/MetaMask/internal-snaps/tree/main/packages/PACKAGE_DIRECTORY_NAME#readme", "bugs": { "url": "https://github.com/MetaMask/internal-snaps/issues" }, @@ -27,8 +27,8 @@ "allow-scripts": "yarn workspace root allow-scripts", "build": "mm-snap build", "build:clean": "yarn clean && yarn build", - "changelog:update": "../../scripts/update-changelog.sh @metamask/sample-snap", - "changelog:validate": "../../scripts/validate-changelog.sh @metamask/sample-snap", + "changelog:update": "../../scripts/update-changelog.sh PACKAGE_NAME", + "changelog:validate": "../../scripts/validate-changelog.sh PACKAGE_NAME", "clean": "rimraf dist", "prepublishOnly": "mm-snap manifest", "serve": "mm-snap serve", @@ -45,14 +45,13 @@ "@metamask/snaps-cli": "^8.4.1", "@metamask/snaps-jest": "^10.2.1", "@metamask/snaps-sdk": "^12.0.1", - "@types/react": "18.2.4", - "@types/react-dom": "18.2.4", "eslint": "^9.39.1", "jest": "30.0.3", "rimraf": "^6.0.1", + "ts-jest": "^29.4.1", "typescript": "~5.8.3" }, "engines": { - "node": ">=20" + "node": "NODE_VERSIONS" } } diff --git a/packages/sample-snap/snap.config.ts b/scripts/create-package/snap-template/snap.config.ts similarity index 100% rename from packages/sample-snap/snap.config.ts rename to scripts/create-package/snap-template/snap.config.ts diff --git a/packages/sample-snap/snap.manifest.json b/scripts/create-package/snap-template/snap.manifest.json similarity index 68% rename from packages/sample-snap/snap.manifest.json rename to scripts/create-package/snap-template/snap.manifest.json index ec38e0e59..d69a94943 100644 --- a/packages/sample-snap/snap.manifest.json +++ b/scripts/create-package/snap-template/snap.manifest.json @@ -1,17 +1,17 @@ { "version": "0.0.0", - "description": "An example Snap written in TypeScript.", - "proposedName": "TypeScript Example", + "description": "PACKAGE_DESCRIPTION", + "proposedName": "PACKAGE_DIRECTORY_NAME", "repository": { "type": "git", "url": "https://github.com/MetaMask/internal-snaps.git" }, "source": { - "shasum": "Sno6Wef4OjbtlPw43N9uwVEHYoNlIjEiAgdEIjqVd4c=", + "shasum": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", "location": { "npm": { "filePath": "dist/bundle.js", - "packageName": "@metamask/sample-snap", + "packageName": "PACKAGE_NAME", "registry": "https://registry.npmjs.org/" } } diff --git a/packages/sample-snap/src/index.test.tsx b/scripts/create-package/snap-template/src/index.test.tsx similarity index 100% rename from packages/sample-snap/src/index.test.tsx rename to scripts/create-package/snap-template/src/index.test.tsx diff --git a/packages/sample-snap/src/index.tsx b/scripts/create-package/snap-template/src/index.tsx similarity index 100% rename from packages/sample-snap/src/index.tsx rename to scripts/create-package/snap-template/src/index.tsx diff --git a/packages/sample-snap/tsconfig.json b/scripts/create-package/snap-template/tsconfig.json similarity index 100% rename from packages/sample-snap/tsconfig.json rename to scripts/create-package/snap-template/tsconfig.json diff --git a/scripts/create-package/utils.test.ts b/scripts/create-package/utils.test.ts index ad8c264f0..076dfb0ea 100644 --- a/scripts/create-package/utils.test.ts +++ b/scripts/create-package/utils.test.ts @@ -1,10 +1,7 @@ -import * as commentJson from 'comment-json'; import execa from 'execa'; import fs from 'fs'; -import path from 'path'; -import { format } from 'prettier'; -import { MonorepoFiles } from './constants'; +import { PackageTypes, TemplateDirectories } from './constants'; import * as fsUtils from './fs-utils'; import type { PackageData } from './utils'; import { finalizeAndWriteData, readMonorepoFiles } from './utils'; @@ -21,10 +18,6 @@ jest.mock('fs', () => ({ jest.mock('execa', () => jest.fn()); -jest.mock('prettier', () => ({ - format: jest.fn(), -})); - jest.mock('./fs-utils', () => ({ readAllFiles: jest.fn(), writeFiles: jest.fn(), @@ -32,59 +25,37 @@ jest.mock('./fs-utils', () => ({ describe('create-package/utils', () => { describe('readMonorepoFiles', () => { - const tsConfig = JSON.stringify({ - references: [{ path: '../packages/foo' }], - }); - const tsConfigBuild = JSON.stringify({ - references: [{ path: '../packages/foo' }], - }); const packageJson = JSON.stringify({ engines: { node: '>=18.0.0' }, }); it('should read the expected monorepo files', async () => { - (fs.promises.readFile as jest.Mock).mockImplementation( - async (filePath: string) => { - switch (path.basename(filePath)) { - case MonorepoFiles.TsConfig: - return tsConfig; - case MonorepoFiles.TsConfigBuild: - return tsConfigBuild; - case MonorepoFiles.PackageJson: - return packageJson; - default: - throw new Error(`Unexpected file: ${path.basename(filePath)}`); - } - }, - ); + (fs.promises.readFile as jest.Mock).mockResolvedValue(packageJson); const monorepoFileData = await readMonorepoFiles(); expect(monorepoFileData).toStrictEqual({ - tsConfig: commentJson.parse(tsConfig), - tsConfigBuild: commentJson.parse(tsConfigBuild), nodeVersions: '>=18.0.0', }); + expect(fs.promises.readFile).toHaveBeenCalledWith( + expect.stringMatching(/package\.json$/u), + 'utf-8', + ); }); }); describe('finalizeAndWriteData', () => { - it('should write the expected files', async () => { + it('should write the expected files for a library package', async () => { const packageData: PackageData = { name: '@metamask/foo', description: 'A foo package.', + type: PackageTypes.Library, directoryName: 'foo', nodeVersions: '>=18.0.0', currentYear: '2023', }; const monorepoFileData = { - tsConfig: { - references: [{ path: './packages/bar' }], - }, - tsConfigBuild: { - references: [{ path: './packages/bar' }], - }, nodeVersions: '>=18.0.0', }; @@ -102,14 +73,12 @@ describe('create-package/utils', () => { 'mock3.file': 'PACKAGE_DESCRIPTION PACKAGE_DIRECTORY_NAME', }); - (format as jest.Mock).mockImplementation((input) => input); - await finalizeAndWriteData(packageData, monorepoFileData); // processTemplateFiles and writeFiles expect(fsUtils.readAllFiles).toHaveBeenCalledTimes(1); expect(fsUtils.readAllFiles).toHaveBeenCalledWith( - expect.stringMatching(/\/package-template$/u), + expect.stringMatching(/\/library-template$/u), ); expect(fsUtils.writeFiles).toHaveBeenCalledTimes(1); @@ -124,36 +93,6 @@ describe('create-package/utils', () => { }, ); - // Writing monorepo files - expect(fs.promises.writeFile).toHaveBeenCalledTimes(2); - expect(format).toHaveBeenCalledTimes(2); - expect(fs.promises.writeFile).toHaveBeenCalledWith( - expect.stringMatching(/tsconfig\.json$/u), - JSON.stringify( - { - references: [ - { path: './packages/bar' }, - { path: './packages/foo' }, - ], - }, - null, - 2, - ), - ); - expect(fs.promises.writeFile).toHaveBeenCalledWith( - expect.stringMatching(/tsconfig\.build\.json$/u), - JSON.stringify( - { - references: [ - { path: './packages/bar' }, - { path: './packages/foo/tsconfig.build.json' }, - ], - }, - null, - 2, - ), - ); - // Postprocessing expect(execa).toHaveBeenCalledTimes(2); expect(execa).toHaveBeenCalledWith('yarn', ['install'], { @@ -164,22 +103,55 @@ describe('create-package/utils', () => { }); }); + it('should read the template files for a snap package from the snap template', async () => { + const packageData: PackageData = { + name: '@metamask/foo-snap', + description: 'A foo snap.', + type: PackageTypes.Snap, + directoryName: 'foo-snap', + nodeVersions: '>=18.0.0', + currentYear: '2023', + }; + + const monorepoFileData = { + nodeVersions: '>=18.0.0', + }; + + const mockError = new Error('Not found') as NodeJS.ErrnoException; + mockError.code = 'ENOENT'; + + jest.spyOn(fs.promises, 'stat').mockRejectedValue(mockError); + + (fsUtils.readAllFiles as jest.Mock).mockResolvedValueOnce({ + 'src/index.tsx': 'export default 42;', + }); + + await finalizeAndWriteData(packageData, monorepoFileData); + + expect(fsUtils.readAllFiles).toHaveBeenCalledTimes(1); + expect(fsUtils.readAllFiles).toHaveBeenCalledWith( + expect.stringMatching(/\/snap-template$/u), + ); + expect(fsUtils.writeFiles).toHaveBeenCalledTimes(1); + expect(fsUtils.writeFiles).toHaveBeenCalledWith( + expect.stringMatching(/packages\/foo-snap$/u), + { + 'src/index.tsx': 'export default 42;', + }, + ); + }); + it('throws if the package directory already exists', async () => { const packageData: PackageData = { name: '@metamask/foo', description: 'A foo package.', + type: PackageTypes.Library, directoryName: 'foo', nodeVersions: '20.0.0', currentYear: '2023', }; const monorepoFileData = { - tsConfig: { - references: [{ path: './packages/bar' }], - }, - tsConfigBuild: { - references: [{ path: './packages/bar' }], - }, nodeVersions: '20.0.0', }; @@ -202,18 +174,13 @@ describe('create-package/utils', () => { const packageData: PackageData = { name: '@metamask/foo', description: 'A foo package.', + type: PackageTypes.Library, directoryName: 'foo', nodeVersions: '20.0.0', currentYear: '2023', }; const monorepoFileData = { - tsConfig: { - references: [{ path: './packages/bar' }], - }, - tsConfigBuild: { - references: [{ path: './packages/bar' }], - }, nodeVersions: '20.0.0', }; @@ -223,3 +190,12 @@ describe('create-package/utils', () => { }); }); }); + +describe('create-package/constants', () => { + it('should have a template directory for every package type', () => { + expect(TemplateDirectories).toStrictEqual({ + snap: 'snap-template', + library: 'library-template', + }); + }); +}); diff --git a/scripts/create-package/utils.ts b/scripts/create-package/utils.ts index eea75955a..36cb0b241 100644 --- a/scripts/create-package/utils.ts +++ b/scripts/create-package/utils.ts @@ -1,18 +1,13 @@ -import * as commentJson from 'comment-json'; import execa from 'execa'; import { promises as fs } from 'fs'; import path from 'path'; -import { format as prettierFormat } from 'prettier'; -import type { Options as PrettierOptions } from 'prettier'; -import { MonorepoFiles, Placeholders } from './constants'; +import type { PackageType } from './constants'; +import { MonorepoFiles, Placeholders, TemplateDirectories } from './constants'; import type { FileMap } from './fs-utils'; import { readAllFiles, writeFiles } from './fs-utils'; -const PACKAGE_TEMPLATE_DIR = path.join(__dirname, 'package-template'); const REPO_ROOT = path.join(__dirname, '..', '..'); -const REPO_TS_CONFIG = path.join(REPO_ROOT, MonorepoFiles.TsConfig); -const REPO_TS_CONFIG_BUILD = path.join(REPO_ROOT, MonorepoFiles.TsConfigBuild); const REPO_PACKAGE_JSON = path.join(REPO_ROOT, MonorepoFiles.PackageJson); const PACKAGES_PATH = path.join(REPO_ROOT, 'packages'); @@ -21,18 +16,13 @@ const allPlaceholdersRegex = new RegExp( 'gu', ); -// Our lint config really hates this, but it works. -// eslint-disable-next-line -const prettierRc = require( - path.join(REPO_ROOT, '.prettierrc.js'), -) as PrettierOptions; - /** * The data necessary to create a new package. */ export type PackageData = Readonly<{ name: string; description: string; + type: PackageType; directoryName: string; nodeVersions: string; currentYear: string; @@ -42,19 +32,9 @@ export type PackageData = Readonly<{ * Data parsed from relevant monorepo files. */ type MonorepoFileData = { - tsConfig: Tsconfig; - tsConfigBuild: Tsconfig; nodeVersions: string; }; -/** - * A parsed tsconfig file. - */ -type Tsconfig = { - references: { path: string }[]; - [key: string]: unknown; -}; - /** * A parsed package.json file. */ @@ -69,15 +49,9 @@ type PackageJson = { * @returns A map of file paths to file contents. */ export async function readMonorepoFiles(): Promise { - const [tsConfig, tsConfigBuild, packageJson] = await Promise.all([ - fs.readFile(REPO_TS_CONFIG, 'utf-8'), - fs.readFile(REPO_TS_CONFIG_BUILD, 'utf-8'), - fs.readFile(REPO_PACKAGE_JSON, 'utf-8'), - ]); + const packageJson = await fs.readFile(REPO_PACKAGE_JSON, 'utf-8'); return { - tsConfig: commentJson.parse(tsConfig) as unknown as Tsconfig, - tsConfigBuild: commentJson.parse(tsConfigBuild) as unknown as Tsconfig, nodeVersions: (JSON.parse(packageJson) as PackageJson).engines.node, }; } @@ -103,20 +77,12 @@ export async function finalizeAndWriteData( } } - console.log('Writing package and monorepo files...'); + console.log('Writing package files...'); // Read and write package files - await writeFiles(packagePath, await processTemplateFiles(packageData)); - - // Write monorepo files - updateTsConfigs(packageData, monorepoFileData); - await writeJsonFile( - REPO_TS_CONFIG, - commentJson.stringify(monorepoFileData.tsConfig, null, 2), - ); - await writeJsonFile( - REPO_TS_CONFIG_BUILD, - commentJson.stringify(monorepoFileData.tsConfigBuild, null, 2), + await writeFiles( + packagePath, + await processTemplateFiles(packageData, monorepoFileData), ); // Postprocess @@ -130,60 +96,30 @@ export async function finalizeAndWriteData( } /** - * Formats a JSON file with `prettier` and writes it to disk. - * - * @param filePath - The absolute path of the file to write. - * @param fileContent - The file content to write. - */ -async function writeJsonFile( - filePath: string, - fileContent: string, -): Promise { - await fs.writeFile( - filePath, - await prettierFormat(fileContent, { ...prettierRc, parser: 'json' }), - ); -} - -/** - * Updates the tsconfig file data in place to include the new package. - * - * @param packageData - = The package data. - * @param monorepoFileData - The monorepo file data. - */ -function updateTsConfigs( - packageData: PackageData, - monorepoFileData: MonorepoFileData, -): void { - const { tsConfig, tsConfigBuild } = monorepoFileData; - - tsConfig.references.push({ - path: `./${path.basename(PACKAGES_PATH)}/${packageData.directoryName}`, - }); - tsConfig.references.sort((a, b) => a.path.localeCompare(b.path)); - - tsConfigBuild.references.push({ - path: `./${path.basename(PACKAGES_PATH)}/${ - packageData.directoryName - }/tsconfig.build.json`, - }); - tsConfigBuild.references.sort((a, b) => a.path.localeCompare(b.path)); -} - -/** - * Reads the template files and updates them with the specified package data. + * Reads the template files for the package type and updates them with the + * specified package data. * * @param packageData - The package data. + * @param monorepoFileData - The monorepo file data. * @returns A map of file paths to processed template file contents. */ async function processTemplateFiles( packageData: PackageData, + monorepoFileData: MonorepoFileData, ): Promise { const result: FileMap = {}; - const templateFiles = await readAllFiles(PACKAGE_TEMPLATE_DIR); + const templateDir = path.join( + __dirname, + TemplateDirectories[packageData.type], + ); + const templateFiles = await readAllFiles(templateDir); for (const [relativePath, content] of Object.entries(templateFiles)) { - result[relativePath] = processTemplateContent(packageData, content); + result[relativePath] = processTemplateContent( + packageData, + monorepoFileData, + content, + ); } return result; @@ -194,14 +130,17 @@ async function processTemplateFiles( * from the specified package data. * * @param packageData - The package data. + * @param monorepoFileData - The monorepo file data. * @param content - The template file content. * @returns The processed template file content. */ function processTemplateContent( packageData: PackageData, + monorepoFileData: MonorepoFileData, content: string, ): string { - const { name, description, nodeVersions, currentYear } = packageData; + const { name, description, currentYear } = packageData; + const { nodeVersions } = monorepoFileData; return content.replace(allPlaceholdersRegex, (match) => { switch (match) { diff --git a/yarn.lock b/yarn.lock index a7cfcbafc..7cc652475 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2701,7 +2701,6 @@ __metadata: "@typescript-eslint/eslint-plugin": "npm:^8.48.0" "@typescript-eslint/parser": "npm:^8.48.0" "@yarnpkg/types": "npm:^4.0.0" - comment-json: "npm:^4.5.1" depcheck: "npm:^1.4.7" eslint: "npm:^9.39.1" eslint-config-prettier: "npm:^9.1.0" @@ -2720,7 +2719,6 @@ __metadata: lodash: "npm:^4.17.21" nock: "npm:^13.3.1" oxfmt: "npm:^0.45.0" - prettier: "npm:^3.3.3" prettier-2: "npm:prettier@^2.8.8" rimraf: "npm:^6.0.1" semver: "npm:^7.6.3" @@ -3287,24 +3285,6 @@ __metadata: languageName: node linkType: hard -"@metamask/sample-snap@workspace:packages/sample-snap": - version: 0.0.0-use.local - resolution: "@metamask/sample-snap@workspace:packages/sample-snap" - dependencies: - "@jest/globals": "npm:^29.5.0" - "@metamask/auto-changelog": "npm:^6.1.1" - "@metamask/snaps-cli": "npm:^8.4.1" - "@metamask/snaps-jest": "npm:^10.2.1" - "@metamask/snaps-sdk": "npm:^12.0.1" - "@types/react": "npm:18.2.4" - "@types/react-dom": "npm:18.2.4" - eslint: "npm:^9.39.1" - jest: "npm:30.0.3" - rimraf: "npm:^6.0.1" - typescript: "npm:~5.8.3" - languageName: unknown - linkType: soft - "@metamask/scure-bip39@npm:^2.1.1": version: 2.1.1 resolution: "@metamask/scure-bip39@npm:2.1.1" @@ -6611,13 +6591,6 @@ __metadata: languageName: node linkType: hard -"array-timsort@npm:^1.0.3": - version: 1.0.3 - resolution: "array-timsort@npm:1.0.3" - checksum: 10/f417f073b3733baec3a80decdf5d45bf763f04676ef3610b0e71f9b1d88c6e4c38154c05b28b31529d308bfd0e043d08059fcd9df966245a1276af15b5584936 - languageName: node - linkType: hard - "array-union@npm:^2.1.0": version: 2.1.0 resolution: "array-union@npm:2.1.0" @@ -7802,16 +7775,6 @@ __metadata: languageName: node linkType: hard -"comment-json@npm:^4.5.1": - version: 4.6.2 - resolution: "comment-json@npm:4.6.2" - dependencies: - array-timsort: "npm:^1.0.3" - esprima: "npm:^4.0.1" - checksum: 10/2f6e79b7ae81a919a0dd5fc2fac84e9ebdaa501a5f3ffe5b9f2fd5dbe94d686d8ee9a6bddf5bae364bce104dbb1c599a9d4184aeb211aaf4a283b8900438d242 - languageName: node - linkType: hard - "comment-parser@npm:1.4.1": version: 1.4.1 resolution: "comment-parser@npm:1.4.1" @@ -8960,7 +8923,7 @@ __metadata: languageName: node linkType: hard -"esprima@npm:^4.0.0, esprima@npm:^4.0.1": +"esprima@npm:^4.0.0": version: 4.0.1 resolution: "esprima@npm:4.0.1" bin: