Skip to content

Commit c2a46ae

Browse files
committed
refactor: Migrate monorepo to ESM, make various tweaks
- test: Add internal test-utils package - chore: Synchronize workspace package dependencies - Accomplished by copying over sections of the Snaps monorepo constraints, which allow the `workspace:^` dependency version. In this way, we will never accidentally pull a workspace package from npm. - build(extension): Externalize endoify.mjs in HTML files - refactor: Upgrade ts-bridge and migrate all packages to ESM - chore: Add "clean" script to all packages - fix: Fix intra-repo path resolution in tests - By means of the plugin `vite-tsconfig-paths`
1 parent 6698e3a commit c2a46ae

32 files changed

+457
-113
lines changed

.depcheckrc.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ ignores:
1111
- 'jsdom'
1212
- 'prettier-plugin-*'
1313
- 'typedoc'
14+
- 'vite-tsconfig-paths'
1415
- 'vite'
1516
- 'vitest'

.eslintrc.js renamed to .eslintrc.cjs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module.exports = {
1212
},
1313

1414
ignorePatterns: [
15-
'!.eslintrc.js',
15+
'!.eslintrc.cjs',
1616
'!vite.config.mts',
1717
'!vitest.config.mts',
1818
'node_modules',
@@ -27,22 +27,30 @@ module.exports = {
2727
// in `@metamask/eslint-config-nodejs` in the future.
2828
'import/no-nodejs-modules': 'off',
2929

30+
'import/no-useless-path-segments': [
31+
'error',
32+
{
33+
// Enabling this causes false errors in ESM files.
34+
noUselessIndex: false,
35+
},
36+
],
37+
3038
// This prevents using Node.js and/or browser specific globals. We
3139
// currently use both in our codebase, so this rule is disabled.
3240
'no-restricted-globals': 'off',
3341
},
3442

3543
overrides: [
3644
{
37-
files: ['*.js', '*.cjs'],
45+
files: ['*.cjs'],
3846
parserOptions: {
3947
sourceType: 'script',
4048
ecmaVersion: '2020',
4149
},
4250
},
4351

4452
{
45-
files: ['*.mjs'],
53+
files: ['*.js', '*.mjs'],
4654
parserOptions: {
4755
sourceType: 'module',
4856
ecmaVersion: '2020',
@@ -70,8 +78,8 @@ module.exports = {
7078
rules: {
7179
'@typescript-eslint/no-explicit-any': 'error',
7280

73-
// This rule is broken, and without the `allowAny` option, it causes
74-
// a lot of false positives.
81+
// This rule is broken, and without the `allowAny` option, it reports a lot
82+
// of false errors.
7583
'@typescript-eslint/restrict-template-expressions': [
7684
'error',
7785
{
@@ -98,6 +106,15 @@ module.exports = {
98106
},
99107
},
100108

109+
{
110+
// Overrides of overrides.
111+
files: ['*'],
112+
rules: {
113+
// This prevents pretty formatting of comments with multi-line lists entries.
114+
'jsdoc/check-indentation': 'off',
115+
},
116+
},
117+
101118
{
102119
// @metamask/eslint-plugin-vitest does not exist, so this is copied from the
103120
// jest-equivalent. All of the rules we specify are the same. Ref:
File renamed without changes.

constraints.pro

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -208,18 +208,18 @@ gen_enforced_field(WorkspaceCwd, 'module', './dist/index.mjs') :-
208208
\+ workspace_field(WorkspaceCwd, 'private', true).
209209
% Non-published packages must not specify an entrypoint.
210210
gen_enforced_field(WorkspaceCwd, 'main', null) :-
211-
WorkspaceCwd \= 'packages/shims',
211+
WorkspaceCwd \= 'packages/shims'.
212212
workspace_field(WorkspaceCwd, 'private', true).
213213
gen_enforced_field(WorkspaceCwd, 'module', null) :-
214-
WorkspaceCwd \= 'packages/shims',
214+
WorkspaceCwd \= 'packages/shims'.
215215
workspace_field(WorkspaceCwd, 'private', true).
216216

217217
% The type definitions entrypoint for all publishable packages must be the same.
218218
gen_enforced_field(WorkspaceCwd, 'types', './dist/index.d.cts') :-
219219
\+ workspace_field(WorkspaceCwd, 'private', true).
220220
% Non-published packages must not specify a type definitions entrypoint.
221221
gen_enforced_field(WorkspaceCwd, 'types', null) :-
222-
WorkspaceCwd \= 'packages/shims',
222+
WorkspaceCwd \= 'packages/shims'.
223223
workspace_field(WorkspaceCwd, 'private', true).
224224

225225
% The exports for all published packages must be the same.
@@ -235,18 +235,18 @@ gen_enforced_field(WorkspaceCwd, 'exports["."].import.types', './dist/index.d.mt
235235
\+ workspace_field(WorkspaceCwd, 'private', true).
236236
% package.json
237237
gen_enforced_field(WorkspaceCwd, 'exports["./package.json"]', './package.json') :-
238-
\+ workspace_field(WorkspaceCwd, 'private', true).
239-
% Non-published packages must not specify exports.
238+
WorkspaceCwd \= 'packages/extension',
239+
WorkspaceCwd \= '.'.
240+
% The root package must not specify exports.
240241
gen_enforced_field(WorkspaceCwd, 'exports', null) :-
241-
WorkspaceCwd \= 'packages/shims',
242-
workspace_field(WorkspaceCwd, 'private', true).
242+
WorkspaceCwd = '.'.
243243

244244
% Published packages must not have side effects.
245245
gen_enforced_field(WorkspaceCwd, 'sideEffects', false) :-
246246
\+ workspace_field(WorkspaceCwd, 'private', true).
247247
% Non-published packages must not specify side effects.
248248
gen_enforced_field(WorkspaceCwd, 'sideEffects', null) :-
249-
WorkspaceCwd \= 'packages/shims',
249+
WorkspaceCwd \= 'packages/shims'.
250250
workspace_field(WorkspaceCwd, 'private', true).
251251

252252
% The list of files included in published packages must only include files
@@ -265,9 +265,14 @@ gen_enforced_field(WorkspaceCwd, 'files', []) :-
265265
% gen_enforced_field(WorkspaceCwd, 'scripts.build', '') :-
266266
% WorkspaceCwd \= '.'.
267267

268-
% All packages except the root and extension must have the same "build:docs" script.
268+
% All packages except the root and other exceptions must have the same "build:docs" script.
269269
gen_enforced_field(WorkspaceCwd, 'scripts.build:docs', 'typedoc') :-
270270
WorkspaceCwd \= 'packages/extension',
271+
WorkspaceCwd \= 'packages/test-utils',
272+
WorkspaceCwd \= '.'.
273+
274+
% All packages except the root must have the same "clean" script.
275+
gen_enforced_field(WorkspaceCwd, 'scripts.clean', 'rimraf ./dist \'./*.tsbuildinfo\'') :-
271276
WorkspaceCwd \= '.'.
272277

273278
% All published packages must have the same "publish:preview" script.
@@ -301,53 +306,56 @@ gen_enforced_field(WorkspaceCwd, 'scripts.changelog:update', CorrectChangelogUpd
301306
% All non-root packages must have the same "test" script.
302307
gen_enforced_field(WorkspaceCwd, 'scripts.test', 'vitest run --config vitest.config.mts') :-
303308
WorkspaceCwd \= 'packages/shims',
309+
WorkspaceCwd \= 'packages/test-utils',
304310
WorkspaceCwd \= '.'.
305311

306312
% All non-root packages must have the same "test:clean" script.
307313
gen_enforced_field(WorkspaceCwd, 'scripts.test:clean', 'yarn test --no-cache --coverage.clean') :-
314+
WorkspaceCwd \= 'packages/test-utils',
308315
WorkspaceCwd \= '.'.
309316

310317
% All non-root packages must have the same "test:dev" script.
311318
gen_enforced_field(WorkspaceCwd, 'scripts.test:dev', 'yarn test --coverage false') :-
319+
WorkspaceCwd \= 'packages/test-utils',
312320
WorkspaceCwd \= '.'.
313321

314322
% All non-root packages must have the same "test:verbose" script.
315323
gen_enforced_field(WorkspaceCwd, 'scripts.test:verbose', 'yarn test --reporter verbose') :-
324+
WorkspaceCwd \= 'packages/test-utils',
316325
WorkspaceCwd \= '.'.
317326

318327
% All non-root packages must have the same "test:watch" script.
319328
gen_enforced_field(WorkspaceCwd, 'scripts.test:watch', 'vitest --config vitest.config.mts') :-
329+
WorkspaceCwd \= 'packages/test-utils',
320330
WorkspaceCwd \= '.'.
321331

322332
% All dependency ranges must be recognizable (this makes it possible to apply
323333
% the next two rules effectively).
324334
gen_enforced_dependency(WorkspaceCwd, DependencyIdent, 'a range optionally starting with ^ or ~', DependencyType) :-
325335
workspace_has_dependency(WorkspaceCwd, DependencyIdent, DependencyRange, DependencyType),
326-
\+ is_valid_version_range(DependencyRange).
327-
328-
% All version ranges used to reference one workspace package in another
329-
% workspace package's `dependencies` or `devDependencies` must be the same.
330-
% Among all references to the same dependency across the monorepo, the one with
331-
% the smallest version range will win. (We handle `peerDependencies` in another
332-
% constraint, as it has slightly different logic.)
336+
\+ (
337+
DependencyRange = '^1.0.0-rc.12'; % is_valid_version_range does not handle rc suffixes
338+
is_valid_version_range(DependencyRange)
339+
).
340+
341+
% All dependency ranges for a package must be synchronized across the monorepo
342+
% (the least version range wins), regardless of which "*dependencies" field
343+
% where the package appears.
333344
gen_enforced_dependency(WorkspaceCwd, DependencyIdent, OtherDependencyRange, DependencyType) :-
334345
workspace_has_dependency(WorkspaceCwd, DependencyIdent, DependencyRange, DependencyType),
335346
workspace_has_dependency(OtherWorkspaceCwd, DependencyIdent, OtherDependencyRange, OtherDependencyType),
336347
WorkspaceCwd \= OtherWorkspaceCwd,
337348
DependencyRange \= OtherDependencyRange,
338-
npm_version_range_out_of_sync(DependencyRange, OtherDependencyRange),
339-
DependencyType \= 'peerDependencies',
340-
OtherDependencyType \= 'peerDependencies'.
341-
342-
% All version ranges used to reference one workspace package in another
343-
% workspace package's `dependencies` or `devDependencies` must match the current
344-
% version of that package. (We handle `peerDependencies` in another rule.)
345-
gen_enforced_dependency(WorkspaceCwd, DependencyIdent, CorrectDependencyRange, DependencyType) :-
346-
DependencyType \= 'peerDependencies',
349+
npm_version_range_out_of_sync(DependencyRange, OtherDependencyRange).
350+
351+
% If a dependency is listed under "dependencies", it should not be listed under
352+
% "devDependencies". We match on the same dependency range so that if a
353+
% dependency is listed under both lists, their versions are synchronized and
354+
% then this constraint will apply and remove the "right" duplicate.
355+
gen_enforced_dependency(WorkspaceCwd, DependencyIdent, null, DependencyType) :-
356+
workspace_has_dependency(WorkspaceCwd, DependencyIdent, DependencyRange, 'dependencies'),
347357
workspace_has_dependency(WorkspaceCwd, DependencyIdent, DependencyRange, DependencyType),
348-
workspace_ident(OtherWorkspaceCwd, DependencyIdent),
349-
workspace_version(OtherWorkspaceCwd, OtherWorkspaceVersion),
350-
atomic_list_concat(['^', OtherWorkspaceVersion], CorrectDependencyRange).
358+
DependencyType == 'devDependencies'.
351359

352360
% If a workspace package is listed under another workspace package's
353361
% `dependencies`, it should not also be listed under its `devDependencies`.

package.json

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"type": "git",
77
"url": "https://github.com/MetaMask/ocap-kernel.git"
88
},
9+
"type": "module",
910
"files": [],
1011
"workspaces": [
1112
"packages/*"
@@ -19,13 +20,13 @@
1920
"build:watch": "yarn run build --watch",
2021
"changelog:update": "yarn workspaces foreach --all --no-private --parallel --interlaced --verbose run changelog:update",
2122
"changelog:validate": "yarn workspaces foreach --all --no-private --parallel --interlaced --verbose run changelog:validate",
22-
"clean": "rimraf dist '**/*.tsbuildinfo'",
23+
"clean": "rimraf '**/*.tsbuildinfo' && yarn workspaces foreach --all --parallel --interlaced --verbose run clean",
2324
"lint": "yarn lint:eslint && yarn lint:misc --check && yarn constraints && yarn lint:dependencies",
2425
"lint:dependencies": "depcheck && yarn dedupe --check",
2526
"lint:dependencies:fix": "depcheck && yarn dedupe",
2627
"lint:eslint": "eslint . --cache --ext js,mjs,cjs,ts,mts,cts",
2728
"lint:fix": "yarn lint:eslint --fix && yarn lint:misc --write && yarn constraints --fix && yarn lint:dependencies:fix",
28-
"lint:misc": "prettier '**/*.json' '**/*.md' '!**/CHANGELOG.old.md' '**/*.yml' '!.yarnrc.yml' '!merged-packages/**' --ignore-path .gitignore",
29+
"lint:misc": "prettier '**/*.json' '**/*.md' '**/*.html' '!**/CHANGELOG.old.md' '**/*.yml' '!.yarnrc.yml' '!merged-packages/**' --ignore-path .gitignore",
2930
"prepack": "./scripts/prepack.sh",
3031
"test": "yarn workspaces foreach --all --parallel --verbose run test",
3132
"test:clean": "yarn workspaces foreach --all --parallel --verbose run test:clean && yarn test",
@@ -39,7 +40,7 @@
3940
"@metamask/eslint-config": "^12.2.0",
4041
"@metamask/eslint-config-nodejs": "^12.1.0",
4142
"@metamask/eslint-config-typescript": "^12.1.0",
42-
"@ts-bridge/cli": "^0.1.4",
43+
"@ts-bridge/cli": "^0.3.0",
4344
"@ts-bridge/shims": "^0.1.1",
4445
"@types/node": "^18.18.14",
4546
"@typescript-eslint/eslint-plugin": "^5.43.0",
@@ -60,7 +61,8 @@
6061
"typedoc": "^0.24.8",
6162
"typescript": "~4.9.5",
6263
"vite": "^5.3.5",
63-
"vitest": "^2.0.4"
64+
"vite-tsconfig-paths": "^4.3.2",
65+
"vitest": "^2.0.5"
6466
},
6567
"packageManager": "[email protected]",
6668
"engines": {

packages/extension/.eslintrc.js renamed to packages/extension/.eslintrc.cjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module.exports = {
2-
extends: ['../../.eslintrc.js'],
2+
extends: ['../../.eslintrc.cjs'],
33

44
overrides: [
55
{

packages/extension/package.json

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"type": "git",
88
"url": "https://github.com/MetaMask/ocap-kernel.git"
99
},
10+
"type": "module",
1011
"files": [
1112
"dist/"
1213
],
@@ -15,6 +16,7 @@
1516
"build:types": "tsc --project tsconfig.build.json",
1617
"build:vite": "vite build --config vite.config.mts",
1718
"changelog:validate": "../../scripts/validate-changelog.sh @ocap/extension",
19+
"clean": "rimraf ./dist './*.tsbuildinfo'",
1820
"publish:preview": "yarn npm publish --tag preview",
1921
"start": "vite --config vite.config.mts & vite build --watch --config vite.config.mts",
2022
"test": "vitest run --config vitest.config.mts",
@@ -28,22 +30,25 @@
2830
"@endo/promise-kit": "^1.1.2",
2931
"@metamask/snaps-utils": "^7.8.0",
3032
"@metamask/utils": "^9.1.0",
31-
"@ocap/shims": "^0.0.0",
32-
"ses": "^1.5.0"
33+
"@ocap/shims": "workspace:^",
34+
"ses": "^1.7.0"
3335
},
3436
"devDependencies": {
3537
"@arethetypeswrong/cli": "^0.15.3",
3638
"@metamask/auto-changelog": "^3.4.4",
39+
"@ocap/test-utils": "workspace:^",
3740
"@types/chrome": "^0.0.268",
38-
"@vitest/coverage-v8": "^2.0.4",
41+
"@vitest/coverage-v8": "^2.0.5",
42+
"cheerio": "^1.0.0-rc.12",
3943
"deepmerge": "^4.3.1",
4044
"jsdom": "^24.1.1",
45+
"prettier": "^2.7.1",
4146
"typedoc": "^0.24.8",
4247
"typedoc-plugin-missing-exports": "^2.0.0",
4348
"typescript": "~4.9.5",
4449
"vite": "^5.3.5",
4550
"vite-plugin-static-copy": "^1.0.6",
46-
"vitest": "^2.0.4"
51+
"vitest": "^2.0.5"
4752
},
4853
"engines": {
4954
"node": "^18.18 || >=20"

packages/extension/src/background.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import './dev-console.mjs';
33
import './endoify.mjs';
44
/* eslint-enable import/extensions,import/no-unassigned-import */
55

6-
import type { ExtensionMessage } from './shared';
7-
import { Command, makeHandledCallback } from './shared';
6+
import type { ExtensionMessage } from './shared.js';
7+
import { Command, makeHandledCallback } from './shared.js';
88

99
// globalThis.kernel will exist due to dev-console.mjs
1010
Object.defineProperties(globalThis.kernel, {

packages/extension/src/iframe-manager.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as snapsUtils from '@metamask/snaps-utils';
22
import { vi, beforeEach, describe, it, expect } from 'vitest';
33

4-
import { Command } from './shared';
4+
import { Command } from './shared.js';
55

66
vi.mock('@endo/promise-kit', () => ({
77
makePromiseKit: () => {
@@ -21,11 +21,11 @@ vi.mock('@metamask/snaps-utils', () => ({
2121

2222
describe('IframeManager', () => {
2323
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
24-
let IframeManager: typeof import('./iframe-manager').IframeManager;
24+
let IframeManager: typeof import('./iframe-manager.js').IframeManager;
2525

2626
beforeEach(async () => {
2727
vi.resetModules();
28-
IframeManager = (await import('./iframe-manager')).IframeManager;
28+
IframeManager = (await import('./iframe-manager.js')).IframeManager;
2929
});
3030

3131
describe('getInstance', () => {

packages/extension/src/iframe-manager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import type { PromiseKit } from '@endo/promise-kit';
22
import { makePromiseKit } from '@endo/promise-kit';
33
import { createWindow } from '@metamask/snaps-utils';
44

5-
import type { IframeMessage } from './shared';
6-
import { Command, isWrappedIframeMessage } from './shared';
5+
import type { IframeMessage } from './shared.js';
6+
import { Command, isWrappedIframeMessage } from './shared.js';
77

88
const IFRAME_URI = 'iframe.html';
99

packages/extension/src/iframe.html

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,5 @@
55
<title>Ocap Iframe</title>
66
<script src="iframe.ts" type="module"></script>
77
</head>
8-
<body>
9-
</body>
8+
<body></body>
109
</html>
11-

packages/extension/src/iframe.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// eslint-disable-next-line import/extensions,import/no-unassigned-import
2-
import './endoify.mjs';
3-
41
import { Command, isWrappedIframeMessage } from './shared.js';
52

63
const defaultCompartment = new Compartment({ URL });

packages/extension/src/offscreen.html

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,5 @@
55
<title>Ocap Offscreen Document</title>
66
<script src="offscreen.ts" type="module"></script>
77
</head>
8-
<body>
9-
</body>
8+
<body></body>
109
</html>
11-

packages/extension/src/offscreen.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
// eslint-disable-next-line import/extensions,import/no-unassigned-import
2-
import './endoify.mjs';
3-
41
import { IframeManager } from './iframe-manager.js';
5-
import type { ExtensionMessage } from './shared';
6-
import { Command, makeHandledCallback } from './shared';
2+
import type { ExtensionMessage } from './shared.js';
3+
import { Command, makeHandledCallback } from './shared.js';
74

85
// Handle messages from the background service worker
96
chrome.runtime.onMessage.addListener(

packages/extension/src/shared.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { vi, describe, it, expect } from 'vitest';
22

3-
import { isWrappedIframeMessage, makeHandledCallback } from './shared';
3+
import { isWrappedIframeMessage, makeHandledCallback } from './shared.js';
44

55
describe('shared', () => {
66
describe('isWrappedIframeMessage', () => {

0 commit comments

Comments
 (0)