@baseplate-dev/core-generators 0.3.7 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants/core-packages.d.ts +1 -0
- package/dist/constants/core-packages.d.ts.map +1 -1
- package/dist/constants/core-packages.js +2 -0
- package/dist/constants/core-packages.js.map +1 -1
- package/dist/constants/node.d.ts +1 -1
- package/dist/constants/node.js +1 -1
- package/dist/generators/node/index.d.ts +3 -0
- package/dist/generators/node/index.d.ts.map +1 -1
- package/dist/generators/node/index.js +3 -0
- package/dist/generators/node/index.js.map +1 -1
- package/dist/generators/node/node/node.generator.d.ts +6 -1
- package/dist/generators/node/node/node.generator.d.ts.map +1 -1
- package/dist/generators/node/node/node.generator.js +17 -3
- package/dist/generators/node/node/node.generator.js.map +1 -1
- package/dist/generators/node/node-git-ignore/node-git-ignore.generator.js +1 -1
- package/dist/generators/node/node-git-ignore/node-git-ignore.generator.js.map +1 -1
- package/dist/generators/node/pnpm-workspace/index.d.ts +2 -0
- package/dist/generators/node/pnpm-workspace/index.d.ts.map +1 -0
- package/dist/generators/node/pnpm-workspace/index.js +2 -0
- package/dist/generators/node/pnpm-workspace/index.js.map +1 -0
- package/dist/generators/node/pnpm-workspace/pnpm-workspace.generator.d.ts +12 -0
- package/dist/generators/node/pnpm-workspace/pnpm-workspace.generator.d.ts.map +1 -0
- package/dist/generators/node/pnpm-workspace/pnpm-workspace.generator.js +56 -0
- package/dist/generators/node/pnpm-workspace/pnpm-workspace.generator.js.map +1 -0
- package/dist/generators/node/prettier/prettier.generator.d.ts +2 -0
- package/dist/generators/node/prettier/prettier.generator.d.ts.map +1 -1
- package/dist/generators/node/prettier/prettier.generator.js +11 -4
- package/dist/generators/node/prettier/prettier.generator.js.map +1 -1
- package/dist/generators/node/root-readme/generated/index.d.ts +26 -0
- package/dist/generators/node/root-readme/generated/index.d.ts.map +1 -0
- package/dist/generators/node/root-readme/generated/index.js +9 -0
- package/dist/generators/node/root-readme/generated/index.js.map +1 -0
- package/dist/generators/node/root-readme/generated/template-paths.d.ts +12 -0
- package/dist/generators/node/root-readme/generated/template-paths.d.ts.map +1 -0
- package/dist/generators/node/root-readme/generated/template-paths.js +20 -0
- package/dist/generators/node/root-readme/generated/template-paths.js.map +1 -0
- package/dist/generators/node/root-readme/generated/template-renderers.d.ts +17 -0
- package/dist/generators/node/root-readme/generated/template-renderers.d.ts.map +1 -0
- package/dist/generators/node/root-readme/generated/template-renderers.js +29 -0
- package/dist/generators/node/root-readme/generated/template-renderers.js.map +1 -0
- package/dist/generators/node/root-readme/generated/typed-templates.d.ts +8 -0
- package/dist/generators/node/root-readme/generated/typed-templates.d.ts.map +1 -0
- package/dist/generators/node/root-readme/generated/typed-templates.js +12 -0
- package/dist/generators/node/root-readme/generated/typed-templates.js.map +1 -0
- package/dist/generators/node/root-readme/index.d.ts +2 -0
- package/dist/generators/node/root-readme/index.d.ts.map +1 -0
- package/dist/generators/node/root-readme/index.js +2 -0
- package/dist/generators/node/root-readme/index.js.map +1 -0
- package/dist/generators/node/root-readme/root-readme.generator.d.ts +23 -0
- package/dist/generators/node/root-readme/root-readme.generator.d.ts.map +1 -0
- package/dist/generators/node/root-readme/root-readme.generator.js +37 -0
- package/dist/generators/node/root-readme/root-readme.generator.js.map +1 -0
- package/dist/generators/node/root-readme/templates/package/README.md +93 -0
- package/dist/generators/node/ts-utils/generated/ts-import-providers.d.ts +1 -2
- package/dist/generators/node/ts-utils/generated/ts-import-providers.d.ts.map +1 -1
- package/dist/generators/node/ts-utils/generated/ts-import-providers.js +1 -1
- package/dist/generators/node/ts-utils/generated/ts-import-providers.js.map +1 -1
- package/dist/generators/node/turbo/index.d.ts +2 -0
- package/dist/generators/node/turbo/index.d.ts.map +1 -0
- package/dist/generators/node/turbo/index.js +2 -0
- package/dist/generators/node/turbo/index.js.map +1 -0
- package/dist/generators/node/turbo/turbo.generator.d.ts +20 -0
- package/dist/generators/node/turbo/turbo.generator.d.ts.map +1 -0
- package/dist/generators/node/turbo/turbo.generator.js +96 -0
- package/dist/generators/node/turbo/turbo.generator.js.map +1 -0
- package/dist/generators/node/vitest/vitest.generator.d.ts.map +1 -1
- package/dist/generators/node/vitest/vitest.generator.js +1 -0
- package/dist/generators/node/vitest/vitest.generator.js.map +1 -1
- package/dist/renderers/typescript/extractor/render-ts-import-providers.js +1 -1
- package/dist/renderers/typescript/extractor/render-ts-import-providers.js.map +1 -1
- package/dist/renderers/typescript/import-maps/ts-import-map.d.ts.map +1 -1
- package/dist/renderers/typescript/import-maps/ts-import-map.js +4 -2
- package/dist/renderers/typescript/import-maps/ts-import-map.js.map +1 -1
- package/dist/renderers/typescript/utils/ts-code-utils.d.ts +6 -2
- package/dist/renderers/typescript/utils/ts-code-utils.d.ts.map +1 -1
- package/dist/renderers/typescript/utils/ts-code-utils.js +2 -1
- package/dist/renderers/typescript/utils/ts-code-utils.js.map +1 -1
- package/dist/test-helpers/import-map-helpers.d.ts +26 -0
- package/dist/test-helpers/import-map-helpers.d.ts.map +1 -0
- package/dist/test-helpers/import-map-helpers.js +33 -0
- package/dist/test-helpers/import-map-helpers.js.map +1 -0
- package/dist/test-helpers/index.d.ts +29 -0
- package/dist/test-helpers/index.d.ts.map +1 -0
- package/dist/test-helpers/index.js +32 -0
- package/dist/test-helpers/index.js.map +1 -0
- package/dist/test-helpers/matchers.d.ts +91 -0
- package/dist/test-helpers/matchers.d.ts.map +1 -0
- package/dist/test-helpers/matchers.js +167 -0
- package/dist/test-helpers/matchers.js.map +1 -0
- package/dist/test-helpers/setup.d.ts +18 -0
- package/dist/test-helpers/setup.d.ts.map +1 -0
- package/dist/test-helpers/setup.js +20 -0
- package/dist/test-helpers/setup.js.map +1 -0
- package/dist/test-helpers/utils.d.ts +55 -0
- package/dist/test-helpers/utils.d.ts.map +1 -0
- package/dist/test-helpers/utils.js +118 -0
- package/dist/test-helpers/utils.js.map +1 -0
- package/package.json +17 -4
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createTsImportMap } from '../renderers/typescript/import-maps/ts-import-map.js';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a test import map with consistent module specifiers for testing
|
|
4
|
+
*
|
|
5
|
+
* Each import will use the pattern `<name>/<importKey>`, making it easy to
|
|
6
|
+
* identify which test module an import comes from
|
|
7
|
+
*
|
|
8
|
+
* @param importSchema - The import map schema to use
|
|
9
|
+
* @param name - Base name for the module (e.g., 'data-utils')
|
|
10
|
+
* @returns A mock import provider for testing
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const schema = createTsImportMapSchema({
|
|
15
|
+
* scalarField: {},
|
|
16
|
+
* relationHelpers: {},
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* const imports = createTestTsImportMap(schema, 'data-utils');
|
|
20
|
+
* // Creates imports:
|
|
21
|
+
* // scalarField -> 'data-utils/scalarField'
|
|
22
|
+
* // relationHelpers -> 'data-utils/relationHelpers'
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export function createTestTsImportMap(importSchema, name) {
|
|
26
|
+
// Build the imports object based on the schema
|
|
27
|
+
const imports = {};
|
|
28
|
+
for (const key of Object.keys(importSchema)) {
|
|
29
|
+
imports[key] = `${name}/${key}`;
|
|
30
|
+
}
|
|
31
|
+
return createTsImportMap(importSchema, imports);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=import-map-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-map-helpers.js","sourceRoot":"","sources":["../../src/test-helpers/import-map-helpers.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sDAAsD,CAAC;AAEzF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,qBAAqB,CAEnC,YAAe,EAAE,IAAY;IAC7B,+CAA+C;IAC/C,MAAM,OAAO,GAA2B,EAAE,CAAC;IAE3C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,IAAI,IAAI,GAAG,EAAE,CAAC;IAClC,CAAC;IAED,OAAO,iBAAiB,CAAC,YAAY,EAAE,OAAkC,CAAC,CAAC;AAC7E,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test helpers for @baseplate-dev/core-generators
|
|
3
|
+
*
|
|
4
|
+
* This module provides utilities for testing code generators, including:
|
|
5
|
+
* - Custom Vitest matchers for TypeScript fragments
|
|
6
|
+
* - Utilities for creating mock import providers
|
|
7
|
+
* - Fragment comparison functions
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { createTestTsImportMap, extendFragmentMatchers } from '@baseplate-dev/core-generators/test-helpers';
|
|
12
|
+
*
|
|
13
|
+
* // Extend Vitest matchers (call once in setup)
|
|
14
|
+
* extendFragmentMatchers();
|
|
15
|
+
*
|
|
16
|
+
* // Create mock import providers for testing
|
|
17
|
+
* const imports = createTestTsImportMap(schema);
|
|
18
|
+
*
|
|
19
|
+
* // Use custom matchers in tests
|
|
20
|
+
* expect(fragment).toMatchTsFragment(expectedFragment);
|
|
21
|
+
* expect(fragment).toIncludeImport('z', 'zod');
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @packageDocumentation
|
|
25
|
+
*/
|
|
26
|
+
export { createTestTsImportMap } from './import-map-helpers.js';
|
|
27
|
+
export { extendFragmentMatchers, fragmentMatchers, type ToIncludeImportOptions, type ToMatchTsFragmentOptions, } from './matchers.js';
|
|
28
|
+
export { areFragmentsEqual, type CompareFragmentsOptions, normalizeFragment, } from './utils.js';
|
|
29
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test-helpers/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,OAAO,EACL,sBAAsB,EACtB,gBAAgB,EAChB,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,GAC9B,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,iBAAiB,EACjB,KAAK,uBAAuB,EAC5B,iBAAiB,GAClB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test helpers for @baseplate-dev/core-generators
|
|
3
|
+
*
|
|
4
|
+
* This module provides utilities for testing code generators, including:
|
|
5
|
+
* - Custom Vitest matchers for TypeScript fragments
|
|
6
|
+
* - Utilities for creating mock import providers
|
|
7
|
+
* - Fragment comparison functions
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { createTestTsImportMap, extendFragmentMatchers } from '@baseplate-dev/core-generators/test-helpers';
|
|
12
|
+
*
|
|
13
|
+
* // Extend Vitest matchers (call once in setup)
|
|
14
|
+
* extendFragmentMatchers();
|
|
15
|
+
*
|
|
16
|
+
* // Create mock import providers for testing
|
|
17
|
+
* const imports = createTestTsImportMap(schema);
|
|
18
|
+
*
|
|
19
|
+
* // Use custom matchers in tests
|
|
20
|
+
* expect(fragment).toMatchTsFragment(expectedFragment);
|
|
21
|
+
* expect(fragment).toIncludeImport('z', 'zod');
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @packageDocumentation
|
|
25
|
+
*/
|
|
26
|
+
// Export import map helpers
|
|
27
|
+
export { createTestTsImportMap } from './import-map-helpers.js';
|
|
28
|
+
// Export matcher functions and types
|
|
29
|
+
export { extendFragmentMatchers, fragmentMatchers, } from './matchers.js';
|
|
30
|
+
// Export utility functions
|
|
31
|
+
export { areFragmentsEqual, normalizeFragment, } from './utils.js';
|
|
32
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/test-helpers/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,4BAA4B;AAC5B,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,qCAAqC;AACrC,OAAO,EACL,sBAAsB,EACtB,gBAAgB,GAGjB,MAAM,eAAe,CAAC;AAEvB,2BAA2B;AAC3B,OAAO,EACL,iBAAiB,EAEjB,iBAAiB,GAClB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { TsCodeFragment } from '../renderers/typescript/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Options for toMatchTsFragment matcher
|
|
4
|
+
*/
|
|
5
|
+
export interface ToMatchTsFragmentOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Whether to ignore hoisted fragments in comparison (default: false)
|
|
8
|
+
*/
|
|
9
|
+
ignoreHoistedFragments?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Options for toIncludeImport matcher
|
|
13
|
+
*/
|
|
14
|
+
export interface ToIncludeImportOptions {
|
|
15
|
+
/**
|
|
16
|
+
* Whether the import should be type-only
|
|
17
|
+
*/
|
|
18
|
+
isTypeOnly?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Custom Vitest matchers for TypeScript code fragments
|
|
22
|
+
*/
|
|
23
|
+
export declare const fragmentMatchers: {
|
|
24
|
+
/**
|
|
25
|
+
* Asserts that a TypeScript fragment matches the expected fragment
|
|
26
|
+
* Compares contents, imports (order-independent), and optionally hoisted fragments
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* expect(actualFragment).toMatchTsFragment(expectedFragment);
|
|
31
|
+
* expect(actualFragment).toMatchTsFragment(expectedFragment, {
|
|
32
|
+
* ignoreHoistedFragments: true
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
toMatchTsFragment(this: {
|
|
37
|
+
isNot: boolean;
|
|
38
|
+
}, received: TsCodeFragment, expected: TsCodeFragment, options?: ToMatchTsFragmentOptions): {
|
|
39
|
+
pass: boolean;
|
|
40
|
+
message: () => string;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Asserts that a fragment includes a specific import
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* expect(fragment).toIncludeImport('z', 'zod');
|
|
48
|
+
* expect(fragment).toIncludeImport('Prisma', '@prisma/client', { isTypeOnly: true });
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
toIncludeImport(this: {
|
|
52
|
+
isNot: boolean;
|
|
53
|
+
}, received: TsCodeFragment, name: string, from: string, options?: ToIncludeImportOptions): {
|
|
54
|
+
pass: boolean;
|
|
55
|
+
message: () => string;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Extends Vitest's expect with custom matchers for TypeScript fragments
|
|
60
|
+
* Call this function once in your test setup to enable the matchers
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* // In test setup file or at the top of test file
|
|
65
|
+
* import { extendFragmentMatchers } from '@baseplate-dev/core-generators/test-helpers';
|
|
66
|
+
*
|
|
67
|
+
* extendFragmentMatchers();
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export declare function extendFragmentMatchers(): void;
|
|
71
|
+
/**
|
|
72
|
+
* TypeScript module augmentation for custom matchers
|
|
73
|
+
* This provides type checking and autocomplete for the custom matchers
|
|
74
|
+
*/
|
|
75
|
+
interface FragmentMatchers<R = unknown> {
|
|
76
|
+
/**
|
|
77
|
+
* Asserts that a TypeScript fragment matches the expected fragment
|
|
78
|
+
* Compares contents, imports (order-independent), and optionally hoisted fragments
|
|
79
|
+
*/
|
|
80
|
+
toMatchTsFragment(expected: TsCodeFragment, options?: ToMatchTsFragmentOptions): R;
|
|
81
|
+
/**
|
|
82
|
+
* Asserts that a fragment includes a specific import
|
|
83
|
+
*/
|
|
84
|
+
toIncludeImport(name: string, from: string, options?: ToIncludeImportOptions): R;
|
|
85
|
+
}
|
|
86
|
+
declare module 'vitest' {
|
|
87
|
+
interface Matchers<T = any> extends FragmentMatchers<T> {
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export {};
|
|
91
|
+
//# sourceMappingURL=matchers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matchers.d.ts","sourceRoot":"","sources":["../../src/test-helpers/matchers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,kCAAkC,CAAC;AAI1C;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC;;OAEG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAuED;;GAEG;AACH,eAAO,MAAM,gBAAgB;IAC3B;;;;;;;;;;;OAWG;4BAEK;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,YACd,cAAc,YACd,cAAc,YACd,wBAAwB;;;;IA6EpC;;;;;;;;OAQG;0BAEK;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,YACd,cAAc,QAClB,MAAM,QACN,MAAM,YACF,sBAAsB;;;;CAiCnC,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAED;;;GAGG;AACH,UAAU,gBAAgB,CAAC,CAAC,GAAG,OAAO;IACpC;;;OAGG;IACH,iBAAiB,CACf,QAAQ,EAAE,cAAc,EACxB,OAAO,CAAC,EAAE,wBAAwB,GACjC,CAAC,CAAC;IACL;;OAEG;IACH,eAAe,CACb,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,sBAAsB,GAC/B,CAAC,CAAC;CACN;AAED,OAAO,QAAQ,QAAQ,CAAC;IAEtB,UAAU,QAAQ,CAAC,CAAC,GAAG,GAAG,CAAE,SAAQ,gBAAgB,CAAC,CAAC,CAAC;KAAG;CAC3D"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { expect } from 'vitest';
|
|
2
|
+
import { areFragmentsEqual, normalizeFragment } from './utils.js';
|
|
3
|
+
/**
|
|
4
|
+
* Formats imports for display in error messages
|
|
5
|
+
*/
|
|
6
|
+
function formatImports(imports) {
|
|
7
|
+
if (!imports || imports.length === 0)
|
|
8
|
+
return '(none)';
|
|
9
|
+
return imports
|
|
10
|
+
.map((imp) => {
|
|
11
|
+
const parts = [];
|
|
12
|
+
if (imp.isTypeOnly)
|
|
13
|
+
parts.push('type');
|
|
14
|
+
if (imp.namespaceImport) {
|
|
15
|
+
parts.push(`* as ${imp.namespaceImport}`);
|
|
16
|
+
}
|
|
17
|
+
if (imp.defaultImport) {
|
|
18
|
+
parts.push(imp.defaultImport);
|
|
19
|
+
}
|
|
20
|
+
if (imp.namedImports && imp.namedImports.length > 0) {
|
|
21
|
+
const namedStr = imp.namedImports
|
|
22
|
+
.map((ni) => (ni.alias ? `${ni.name} as ${ni.alias}` : ni.name))
|
|
23
|
+
.join(', ');
|
|
24
|
+
parts.push(`{ ${namedStr} }`);
|
|
25
|
+
}
|
|
26
|
+
return ` ${parts.join(' ')} from '${imp.moduleSpecifier}'`;
|
|
27
|
+
})
|
|
28
|
+
.join('\n');
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Formats hoisted fragments for display in error messages
|
|
32
|
+
*/
|
|
33
|
+
function formatHoistedFragments(fragments) {
|
|
34
|
+
if (!fragments || fragments.length === 0)
|
|
35
|
+
return '(none)';
|
|
36
|
+
return fragments
|
|
37
|
+
.map((frag) => ` [${frag.key}]: ${frag.contents}`)
|
|
38
|
+
.join('\n');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Checks if an import declaration contains a specific named import
|
|
42
|
+
*/
|
|
43
|
+
function hasNamedImport(imp, name, options) {
|
|
44
|
+
if (!imp.namedImports)
|
|
45
|
+
return false;
|
|
46
|
+
return imp.namedImports.some((ni) => {
|
|
47
|
+
if (ni.name !== name)
|
|
48
|
+
return false;
|
|
49
|
+
// If isTypeOnly is specified in options, check it matches
|
|
50
|
+
if (options?.isTypeOnly !== undefined) {
|
|
51
|
+
// Check both the named import level and the declaration level
|
|
52
|
+
const isImportTypeOnly = ni.isTypeOnly ?? imp.isTypeOnly ?? false;
|
|
53
|
+
return isImportTypeOnly === options.isTypeOnly;
|
|
54
|
+
}
|
|
55
|
+
return true;
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Custom Vitest matchers for TypeScript code fragments
|
|
60
|
+
*/
|
|
61
|
+
export const fragmentMatchers = {
|
|
62
|
+
/**
|
|
63
|
+
* Asserts that a TypeScript fragment matches the expected fragment
|
|
64
|
+
* Compares contents, imports (order-independent), and optionally hoisted fragments
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* expect(actualFragment).toMatchTsFragment(expectedFragment);
|
|
69
|
+
* expect(actualFragment).toMatchTsFragment(expectedFragment, {
|
|
70
|
+
* ignoreHoistedFragments: true
|
|
71
|
+
* });
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
toMatchTsFragment(received, expected, options) {
|
|
75
|
+
const { isNot } = this;
|
|
76
|
+
// Normalize both fragments for comparison
|
|
77
|
+
const normalizedReceived = normalizeFragment(received, {
|
|
78
|
+
compareHoistedFragments: !options?.ignoreHoistedFragments,
|
|
79
|
+
});
|
|
80
|
+
const normalizedExpected = normalizeFragment(expected, {
|
|
81
|
+
compareHoistedFragments: !options?.ignoreHoistedFragments,
|
|
82
|
+
});
|
|
83
|
+
// Check equality
|
|
84
|
+
const pass = areFragmentsEqual(received, expected, {
|
|
85
|
+
compareHoistedFragments: !options?.ignoreHoistedFragments,
|
|
86
|
+
});
|
|
87
|
+
return {
|
|
88
|
+
pass,
|
|
89
|
+
message: () => {
|
|
90
|
+
if (pass && isNot) {
|
|
91
|
+
return 'Expected fragments not to be equal';
|
|
92
|
+
}
|
|
93
|
+
const messages = ['Expected fragments to be equal'];
|
|
94
|
+
// Check contents
|
|
95
|
+
if (normalizedReceived.contents !== normalizedExpected.contents) {
|
|
96
|
+
messages.push('', 'Contents:', ` Expected: ${normalizedExpected.contents}`, ` Received: ${normalizedReceived.contents}`);
|
|
97
|
+
}
|
|
98
|
+
// Check imports
|
|
99
|
+
const receivedImportsStr = formatImports(normalizedReceived.imports);
|
|
100
|
+
const expectedImportsStr = formatImports(normalizedExpected.imports);
|
|
101
|
+
if (receivedImportsStr !== expectedImportsStr) {
|
|
102
|
+
messages.push('', 'Imports:', 'Expected:', expectedImportsStr, 'Received:', receivedImportsStr);
|
|
103
|
+
}
|
|
104
|
+
// Check hoisted fragments if not ignored
|
|
105
|
+
if (!options?.ignoreHoistedFragments) {
|
|
106
|
+
const receivedHoistedStr = formatHoistedFragments(normalizedReceived.hoistedFragments);
|
|
107
|
+
const expectedHoistedStr = formatHoistedFragments(normalizedExpected.hoistedFragments);
|
|
108
|
+
if (receivedHoistedStr !== expectedHoistedStr) {
|
|
109
|
+
messages.push('', 'Hoisted Fragments:', 'Expected:', expectedHoistedStr, 'Received:', receivedHoistedStr);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return messages.join('\n');
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
/**
|
|
117
|
+
* Asserts that a fragment includes a specific import
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```typescript
|
|
121
|
+
* expect(fragment).toIncludeImport('z', 'zod');
|
|
122
|
+
* expect(fragment).toIncludeImport('Prisma', '@prisma/client', { isTypeOnly: true });
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
toIncludeImport(received, name, from, options) {
|
|
126
|
+
const { isNot } = this;
|
|
127
|
+
const imports = received.imports ?? [];
|
|
128
|
+
const matchingImport = imports.find((imp) => imp.moduleSpecifier === from);
|
|
129
|
+
const pass = matchingImport !== undefined &&
|
|
130
|
+
hasNamedImport(matchingImport, name, options);
|
|
131
|
+
return {
|
|
132
|
+
pass,
|
|
133
|
+
message: () => {
|
|
134
|
+
if (pass && isNot) {
|
|
135
|
+
const typeOnlyStr = options?.isTypeOnly ? ' (type-only)' : '';
|
|
136
|
+
return `Expected not to include import "${name}"${typeOnlyStr} from "${from}"`;
|
|
137
|
+
}
|
|
138
|
+
const typeOnlyStr = options?.isTypeOnly ? ' (type-only)' : '';
|
|
139
|
+
const foundFrom = matchingImport
|
|
140
|
+
? `, but found import from "${matchingImport.moduleSpecifier}"`
|
|
141
|
+
: '';
|
|
142
|
+
return [
|
|
143
|
+
`Expected to include import "${name}"${typeOnlyStr} from "${from}"${foundFrom}`,
|
|
144
|
+
'',
|
|
145
|
+
'Available imports:',
|
|
146
|
+
formatImports(imports),
|
|
147
|
+
].join('\n');
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* Extends Vitest's expect with custom matchers for TypeScript fragments
|
|
154
|
+
* Call this function once in your test setup to enable the matchers
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```typescript
|
|
158
|
+
* // In test setup file or at the top of test file
|
|
159
|
+
* import { extendFragmentMatchers } from '@baseplate-dev/core-generators/test-helpers';
|
|
160
|
+
*
|
|
161
|
+
* extendFragmentMatchers();
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
export function extendFragmentMatchers() {
|
|
165
|
+
expect.extend(fragmentMatchers);
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=matchers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matchers.js","sourceRoot":"","sources":["../../src/test-helpers/matchers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-setup file for test helpers
|
|
3
|
+
*
|
|
4
|
+
* This file automatically extends Vitest matchers when imported.
|
|
5
|
+
* Add this to your vitest.config.ts setupFiles to enable custom matchers globally.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* // vitest.config.ts
|
|
10
|
+
* export default defineConfig({
|
|
11
|
+
* test: {
|
|
12
|
+
* setupFiles: ['@baseplate-dev/core-generators/test-helpers/setup']
|
|
13
|
+
* }
|
|
14
|
+
* });
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/test-helpers/setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-setup file for test helpers
|
|
3
|
+
*
|
|
4
|
+
* This file automatically extends Vitest matchers when imported.
|
|
5
|
+
* Add this to your vitest.config.ts setupFiles to enable custom matchers globally.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* // vitest.config.ts
|
|
10
|
+
* export default defineConfig({
|
|
11
|
+
* test: {
|
|
12
|
+
* setupFiles: ['@baseplate-dev/core-generators/test-helpers/setup']
|
|
13
|
+
* }
|
|
14
|
+
* });
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
import { extendFragmentMatchers } from './matchers.js';
|
|
18
|
+
// Automatically extend matchers when this file is imported
|
|
19
|
+
extendFragmentMatchers();
|
|
20
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/test-helpers/setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAEvD,2DAA2D;AAC3D,sBAAsB,EAAE,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { TsCodeFragment, TsHoistedFragment, TsImportDeclaration } from '../renderers/typescript/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Options for fragment comparison
|
|
4
|
+
*/
|
|
5
|
+
export interface CompareFragmentsOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Whether to compare hoisted fragments (default: true)
|
|
8
|
+
*/
|
|
9
|
+
compareHoistedFragments?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Normalizes imports for comparison by sorting them in a deterministic order
|
|
13
|
+
* This allows order-independent comparison of imports
|
|
14
|
+
*
|
|
15
|
+
* @param imports - Array of import declarations to normalize
|
|
16
|
+
* @returns Normalized array of import declarations
|
|
17
|
+
*/
|
|
18
|
+
export declare function normalizeImports(imports: TsImportDeclaration[]): TsImportDeclaration[];
|
|
19
|
+
/**
|
|
20
|
+
* Normalizes hoisted fragments for comparison by sorting them by key
|
|
21
|
+
*
|
|
22
|
+
* @param fragments - Array of hoisted fragments to normalize
|
|
23
|
+
* @returns Normalized array of hoisted fragments
|
|
24
|
+
*/
|
|
25
|
+
export declare function normalizeHoistedFragments(fragments?: TsHoistedFragment[]): TsHoistedFragment[] | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Normalizes a code fragment for comparison
|
|
28
|
+
* This includes trimming contents, sorting imports, and normalizing hoisted fragments
|
|
29
|
+
*
|
|
30
|
+
* @param fragment - The fragment to normalize
|
|
31
|
+
* @param options - Options for normalization
|
|
32
|
+
* @returns Normalized fragment
|
|
33
|
+
*/
|
|
34
|
+
export declare function normalizeFragment(fragment: TsCodeFragment, options?: CompareFragmentsOptions): TsCodeFragment;
|
|
35
|
+
/**
|
|
36
|
+
* Compares two TsCodeFragment objects for equality, ignoring import order
|
|
37
|
+
* This is useful for programmatic checks outside of test assertions
|
|
38
|
+
*
|
|
39
|
+
* @param actual - The actual fragment
|
|
40
|
+
* @param expected - The expected fragment
|
|
41
|
+
* @param options - Comparison options
|
|
42
|
+
* @returns True if fragments are equal
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* const actual = tsCodeFragment('foo()', ...);
|
|
47
|
+
* const expected = tsCodeFragment('foo()', ...);
|
|
48
|
+
*
|
|
49
|
+
* if (areFragmentsEqual(actual, expected)) {
|
|
50
|
+
* console.log('Fragments match!');
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare function areFragmentsEqual(actual: TsCodeFragment, expected: TsCodeFragment, options?: CompareFragmentsOptions): boolean;
|
|
55
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/test-helpers/utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EACjB,mBAAmB,EACpB,MAAM,kCAAkC,CAAC;AAE1C;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,mBAAmB,EAAE,GAC7B,mBAAmB,EAAE,CAmCvB;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,CAAC,EAAE,iBAAiB,EAAE,GAC9B,iBAAiB,EAAE,GAAG,SAAS,CAajC;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,cAAc,EACxB,OAAO,CAAC,EAAE,uBAAuB,GAChC,cAAc,CAqBhB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,cAAc,EACxB,OAAO,CAAC,EAAE,uBAAuB,GAChC,OAAO,CAKT"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
2
|
+
/**
|
|
3
|
+
* Normalizes imports for comparison by sorting them in a deterministic order
|
|
4
|
+
* This allows order-independent comparison of imports
|
|
5
|
+
*
|
|
6
|
+
* @param imports - Array of import declarations to normalize
|
|
7
|
+
* @returns Normalized array of import declarations
|
|
8
|
+
*/
|
|
9
|
+
export function normalizeImports(imports) {
|
|
10
|
+
return imports
|
|
11
|
+
.map((imp) => ({
|
|
12
|
+
...imp,
|
|
13
|
+
// Sort named imports alphabetically
|
|
14
|
+
namedImports: imp.namedImports?.slice().sort((a, b) => {
|
|
15
|
+
const nameCompare = a.name.localeCompare(b.name);
|
|
16
|
+
if (nameCompare !== 0)
|
|
17
|
+
return nameCompare;
|
|
18
|
+
// If names are equal, sort by alias
|
|
19
|
+
return (a.alias ?? '').localeCompare(b.alias ?? '');
|
|
20
|
+
}),
|
|
21
|
+
}))
|
|
22
|
+
.sort((a, b) => {
|
|
23
|
+
// Primary sort: module specifier
|
|
24
|
+
const moduleCompare = a.moduleSpecifier.localeCompare(b.moduleSpecifier);
|
|
25
|
+
if (moduleCompare !== 0)
|
|
26
|
+
return moduleCompare;
|
|
27
|
+
// Secondary sort: import type (namespace > default > named)
|
|
28
|
+
const getImportTypeOrder = (imp) => {
|
|
29
|
+
if (imp.namespaceImport)
|
|
30
|
+
return 1;
|
|
31
|
+
if (imp.defaultImport)
|
|
32
|
+
return 2;
|
|
33
|
+
if (imp.namedImports)
|
|
34
|
+
return 3;
|
|
35
|
+
return 4;
|
|
36
|
+
};
|
|
37
|
+
const typeOrderA = getImportTypeOrder(a);
|
|
38
|
+
const typeOrderB = getImportTypeOrder(b);
|
|
39
|
+
if (typeOrderA !== typeOrderB)
|
|
40
|
+
return typeOrderA - typeOrderB;
|
|
41
|
+
// Tertiary sort: isTypeOnly (type-only imports first)
|
|
42
|
+
if (a.isTypeOnly && !b.isTypeOnly)
|
|
43
|
+
return -1;
|
|
44
|
+
if (!a.isTypeOnly && b.isTypeOnly)
|
|
45
|
+
return 1;
|
|
46
|
+
return 0;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Normalizes hoisted fragments for comparison by sorting them by key
|
|
51
|
+
*
|
|
52
|
+
* @param fragments - Array of hoisted fragments to normalize
|
|
53
|
+
* @returns Normalized array of hoisted fragments
|
|
54
|
+
*/
|
|
55
|
+
export function normalizeHoistedFragments(fragments) {
|
|
56
|
+
if (!fragments || fragments.length === 0)
|
|
57
|
+
return undefined;
|
|
58
|
+
return [...fragments]
|
|
59
|
+
.map((frag) => {
|
|
60
|
+
const normalized = normalizeFragment(frag);
|
|
61
|
+
// Preserve the key property from the original hoisted fragment
|
|
62
|
+
return {
|
|
63
|
+
...normalized,
|
|
64
|
+
key: frag.key,
|
|
65
|
+
};
|
|
66
|
+
})
|
|
67
|
+
.sort((a, b) => a.key.localeCompare(b.key));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Normalizes a code fragment for comparison
|
|
71
|
+
* This includes trimming contents, sorting imports, and normalizing hoisted fragments
|
|
72
|
+
*
|
|
73
|
+
* @param fragment - The fragment to normalize
|
|
74
|
+
* @param options - Options for normalization
|
|
75
|
+
* @returns Normalized fragment
|
|
76
|
+
*/
|
|
77
|
+
export function normalizeFragment(fragment, options) {
|
|
78
|
+
const normalized = {
|
|
79
|
+
contents: fragment.contents.trim(),
|
|
80
|
+
};
|
|
81
|
+
// Only include imports if they exist
|
|
82
|
+
if (fragment.imports && fragment.imports.length > 0) {
|
|
83
|
+
normalized.imports = normalizeImports(fragment.imports);
|
|
84
|
+
}
|
|
85
|
+
// Only include hoisted fragments if they exist and should be compared
|
|
86
|
+
if (options?.compareHoistedFragments !== false && fragment.hoistedFragments) {
|
|
87
|
+
const normalizedHoisted = normalizeHoistedFragments(fragment.hoistedFragments);
|
|
88
|
+
if (normalizedHoisted) {
|
|
89
|
+
normalized.hoistedFragments = normalizedHoisted;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return normalized;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Compares two TsCodeFragment objects for equality, ignoring import order
|
|
96
|
+
* This is useful for programmatic checks outside of test assertions
|
|
97
|
+
*
|
|
98
|
+
* @param actual - The actual fragment
|
|
99
|
+
* @param expected - The expected fragment
|
|
100
|
+
* @param options - Comparison options
|
|
101
|
+
* @returns True if fragments are equal
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* const actual = tsCodeFragment('foo()', ...);
|
|
106
|
+
* const expected = tsCodeFragment('foo()', ...);
|
|
107
|
+
*
|
|
108
|
+
* if (areFragmentsEqual(actual, expected)) {
|
|
109
|
+
* console.log('Fragments match!');
|
|
110
|
+
* }
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export function areFragmentsEqual(actual, expected, options) {
|
|
114
|
+
const normalizedActual = normalizeFragment(actual, options);
|
|
115
|
+
const normalizedExpected = normalizeFragment(expected, options);
|
|
116
|
+
return isDeepStrictEqual(normalizedActual, normalizedExpected);
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/test-helpers/utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@baseplate-dev/core-generators",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Core generators for Baseplate",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"code-generation",
|
|
@@ -35,6 +35,18 @@
|
|
|
35
35
|
"./extractors": {
|
|
36
36
|
"types": "./dist/renderers/extractors.d.ts",
|
|
37
37
|
"default": "./dist/renderers/extractors.js"
|
|
38
|
+
},
|
|
39
|
+
"./test-helpers": {
|
|
40
|
+
"types": "./dist/test-helpers/index.d.ts",
|
|
41
|
+
"default": "./dist/test-helpers/index.js"
|
|
42
|
+
},
|
|
43
|
+
"./test-helpers/setup": {
|
|
44
|
+
"types": "./dist/test-helpers/setup.d.ts",
|
|
45
|
+
"default": "./dist/test-helpers/setup.js"
|
|
46
|
+
},
|
|
47
|
+
"./test-helpers/vitest-types": {
|
|
48
|
+
"types": "./dist/test-helpers/vitest-types.d.ts",
|
|
49
|
+
"default": "./dist/test-helpers/vitest-types.js"
|
|
38
50
|
}
|
|
39
51
|
},
|
|
40
52
|
"main": "dist/index.js",
|
|
@@ -58,9 +70,10 @@
|
|
|
58
70
|
"semver": "^7.5.4",
|
|
59
71
|
"sort-package-json": "2.10.1",
|
|
60
72
|
"ts-morph": "26.0.0",
|
|
73
|
+
"yaml": "2.8.1",
|
|
61
74
|
"zod": "3.25.76",
|
|
62
|
-
"@baseplate-dev/sync": "0.
|
|
63
|
-
"@baseplate-dev/utils": "0.
|
|
75
|
+
"@baseplate-dev/sync": "0.4.0",
|
|
76
|
+
"@baseplate-dev/utils": "0.4.0"
|
|
64
77
|
},
|
|
65
78
|
"devDependencies": {
|
|
66
79
|
"@types/node": "^22.17.2",
|
|
@@ -71,7 +84,7 @@
|
|
|
71
84
|
"eslint": "9.32.0",
|
|
72
85
|
"typescript": "5.8.3",
|
|
73
86
|
"vitest": "3.2.4",
|
|
74
|
-
"@baseplate-dev/tools": "0.
|
|
87
|
+
"@baseplate-dev/tools": "0.4.0"
|
|
75
88
|
},
|
|
76
89
|
"engines": {
|
|
77
90
|
"node": "^22.0.0"
|