@kamaalio/codemod-kit 0.0.1
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/README.md +116 -0
- package/dist/codemods/constants.d.ts +1 -0
- package/dist/codemods/index.d.ts +2 -0
- package/dist/codemods/types.d.ts +20 -0
- package/dist/codemods/utils.d.ts +15 -0
- package/dist/index.cjs +137 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +88 -0
- package/dist/utils/collections.d.ts +3 -0
- package/dist/utils/type-utils.d.ts +1 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Codemod kit
|
|
2
|
+
|
|
3
|
+
A toolkit to run codemods.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @kamaalio/codemod-kit
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { runCodemods } from '@kamaalio/codemod-kit';
|
|
15
|
+
import type { CodeMod } from '@kamaalio/codemod-kit';
|
|
16
|
+
|
|
17
|
+
const myCodemod: CodeMod = {
|
|
18
|
+
name: 'my-codemod',
|
|
19
|
+
languages: ['typescript'],
|
|
20
|
+
commitMessage: 'feat(codemod): my first codemod',
|
|
21
|
+
transformer: async (content, filename) => {
|
|
22
|
+
// ... transform the content
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
runCodemods([myCodemod], './src');
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## API
|
|
30
|
+
|
|
31
|
+
### `runCodemods(codemods, transformationPath, options?)`
|
|
32
|
+
|
|
33
|
+
Runs a list of codemods on a given path.
|
|
34
|
+
|
|
35
|
+
- `codemods`: An array of `Codemod` objects.
|
|
36
|
+
- `transformationPath`: The path to the directory to transform.
|
|
37
|
+
- `options`: Optional configuration for the run.
|
|
38
|
+
|
|
39
|
+
### `runCodemod(codemod, transformationPath, globItems, options?)`
|
|
40
|
+
|
|
41
|
+
Runs a single codemod.
|
|
42
|
+
|
|
43
|
+
- `codemod`: A `Codemod` object.
|
|
44
|
+
- `transformationPath`: The path to the directory to transform.
|
|
45
|
+
- `globItems`: An array of file paths to transform.
|
|
46
|
+
- `options`: Optional configuration for the run.
|
|
47
|
+
|
|
48
|
+
### `Codemod`
|
|
49
|
+
|
|
50
|
+
A codemod is defined by the `Codemod` type:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
export type Codemod = {
|
|
54
|
+
name: string;
|
|
55
|
+
languages: Set<NapiLang> | Array<NapiLang>;
|
|
56
|
+
commitMessage: string;
|
|
57
|
+
transformer: (content: SgRoot<TypesMap> | string, filename?: Optional<string>) => Promise<Modifications>;
|
|
58
|
+
};
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- `name`: The name of the codemod.
|
|
62
|
+
- `languages`: The languages the codemod applies to.
|
|
63
|
+
- `commitMessage`: The commit message to use when applying the codemod.
|
|
64
|
+
- `transformer`: The function that transforms the code.
|
|
65
|
+
|
|
66
|
+
### `Modifications`
|
|
67
|
+
|
|
68
|
+
The `transformer` function returns a `Modifications` object:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
export type Modifications = {
|
|
72
|
+
ast: SgRoot<TypesMap>;
|
|
73
|
+
report: ModificationsReport;
|
|
74
|
+
lang: NapiLang;
|
|
75
|
+
filename: Optional<string>;
|
|
76
|
+
history: Array<SgRoot<TypesMap>>;
|
|
77
|
+
};
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
- `ast`: The modified AST.
|
|
81
|
+
- `report`: A report of the changes.
|
|
82
|
+
- `lang`: The language of the file.
|
|
83
|
+
- `filename`: The name of the file.
|
|
84
|
+
- `history`: A history of the modifications.
|
|
85
|
+
|
|
86
|
+
## Hooks
|
|
87
|
+
|
|
88
|
+
You can provide hooks to customize the codemod run:
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
type RunCodemodHooks = {
|
|
92
|
+
targetFiltering?: (filepath: string) => boolean;
|
|
93
|
+
preCodemodRun?: (codemod: Codemod) => Promise<void>;
|
|
94
|
+
postTransform?: (transformedContent: string) => Promise<string>;
|
|
95
|
+
};
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
- `targetFiltering`: A function to filter the files to transform.
|
|
99
|
+
- `preCodemodRun`: A function to run before each codemod.
|
|
100
|
+
- `postTransform`: A function to run after each transformation.
|
|
101
|
+
|
|
102
|
+
## Options
|
|
103
|
+
|
|
104
|
+
You can provide options to customize the codemod run:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
type RunCodemodOptions = {
|
|
108
|
+
hooks?: RunCodemodHooks;
|
|
109
|
+
log?: boolean;
|
|
110
|
+
dry?: boolean;
|
|
111
|
+
};
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
- `hooks`: The hooks to use.
|
|
115
|
+
- `log`: Whether to log the output.
|
|
116
|
+
- `dry`: Whether to run in dry mode (no changes are written to disk).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const LANG_TO_EXTENSIONS_MAPPING: Partial<Record<string, Set<string>>>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { SgRoot } from '@ast-grep/napi';
|
|
2
|
+
import type { NapiLang } from '@ast-grep/napi/types/lang.js';
|
|
3
|
+
import type { TypesMap } from '@ast-grep/napi/types/staticTypes.js';
|
|
4
|
+
import type { Optional } from '../utils/type-utils';
|
|
5
|
+
export type Codemod = {
|
|
6
|
+
name: string;
|
|
7
|
+
languages: Set<NapiLang> | Array<NapiLang>;
|
|
8
|
+
commitMessage: string;
|
|
9
|
+
transformer: (content: SgRoot<TypesMap> | string, filename?: Optional<string>) => Promise<Modifications>;
|
|
10
|
+
};
|
|
11
|
+
export type ModificationsReport = {
|
|
12
|
+
changesApplied: number;
|
|
13
|
+
};
|
|
14
|
+
export type Modifications = {
|
|
15
|
+
ast: SgRoot<TypesMap>;
|
|
16
|
+
report: ModificationsReport;
|
|
17
|
+
lang: NapiLang;
|
|
18
|
+
filename: Optional<string>;
|
|
19
|
+
history: Array<SgRoot<TypesMap>>;
|
|
20
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Result } from 'neverthrow';
|
|
2
|
+
import type { Codemod, Modifications } from './types';
|
|
3
|
+
type RunCodemodHooks = {
|
|
4
|
+
targetFiltering?: (filepath: string) => boolean;
|
|
5
|
+
preCodemodRun?: (codemod: Codemod) => Promise<void>;
|
|
6
|
+
postTransform?: (transformedContent: string) => Promise<string>;
|
|
7
|
+
};
|
|
8
|
+
type RunCodemodOptions = {
|
|
9
|
+
hooks?: RunCodemodHooks;
|
|
10
|
+
log?: boolean;
|
|
11
|
+
dry?: boolean;
|
|
12
|
+
};
|
|
13
|
+
export declare function runCodemods(codemods: Array<Codemod>, transformationPath: string, options?: RunCodemodOptions): Promise<Record<string, Array<Result<Modifications, Error>>>>;
|
|
14
|
+
export declare function runCodemod(codemod: Codemod, transformationPath: string, globItems: Array<string>, options?: RunCodemodOptions): Promise<Array<Result<Modifications, Error>>>;
|
|
15
|
+
export {};
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.n = (module)=>{
|
|
5
|
+
var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
|
|
6
|
+
__webpack_require__.d(getter, {
|
|
7
|
+
a: getter
|
|
8
|
+
});
|
|
9
|
+
return getter;
|
|
10
|
+
};
|
|
11
|
+
})();
|
|
12
|
+
(()=>{
|
|
13
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
14
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
get: definition[key]
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
})();
|
|
20
|
+
(()=>{
|
|
21
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
22
|
+
})();
|
|
23
|
+
(()=>{
|
|
24
|
+
__webpack_require__.r = (exports1)=>{
|
|
25
|
+
if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
26
|
+
value: 'Module'
|
|
27
|
+
});
|
|
28
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
29
|
+
value: true
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
})();
|
|
33
|
+
var __webpack_exports__ = {};
|
|
34
|
+
__webpack_require__.r(__webpack_exports__);
|
|
35
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
36
|
+
runCodemod: ()=>runCodemod,
|
|
37
|
+
runCodemods: ()=>runCodemods
|
|
38
|
+
});
|
|
39
|
+
const external_node_path_namespaceObject = require("node:path");
|
|
40
|
+
var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
|
|
41
|
+
const promises_namespaceObject = require("node:fs/promises");
|
|
42
|
+
var promises_default = /*#__PURE__*/ __webpack_require__.n(promises_namespaceObject);
|
|
43
|
+
const external_fast_glob_namespaceObject = require("fast-glob");
|
|
44
|
+
var external_fast_glob_default = /*#__PURE__*/ __webpack_require__.n(external_fast_glob_namespaceObject);
|
|
45
|
+
const external_neverthrow_namespaceObject = require("neverthrow");
|
|
46
|
+
const napi_namespaceObject = require("@ast-grep/napi");
|
|
47
|
+
const LANG_TO_EXTENSIONS_MAPPING = {
|
|
48
|
+
[napi_namespaceObject.Lang.TypeScript.toLowerCase()]: new Set([
|
|
49
|
+
'.ts',
|
|
50
|
+
'.tsx',
|
|
51
|
+
'.js',
|
|
52
|
+
'.jsx',
|
|
53
|
+
'.cjs',
|
|
54
|
+
'.mjs',
|
|
55
|
+
'.mts'
|
|
56
|
+
])
|
|
57
|
+
};
|
|
58
|
+
function getCollectionCount(collection) {
|
|
59
|
+
if (Array.isArray(collection)) return collection.length;
|
|
60
|
+
return collection.size;
|
|
61
|
+
}
|
|
62
|
+
function collectionIsEmpty(collection) {
|
|
63
|
+
return 0 === getCollectionCount(collection);
|
|
64
|
+
}
|
|
65
|
+
async function runCodemods(codemods, transformationPath, options) {
|
|
66
|
+
const globItems = await external_fast_glob_default().glob([
|
|
67
|
+
'**/*'
|
|
68
|
+
], {
|
|
69
|
+
cwd: transformationPath
|
|
70
|
+
});
|
|
71
|
+
const results = {};
|
|
72
|
+
for (const codemod of codemods)results[codemod.name] = await runCodemod(codemod, transformationPath, globItems, options);
|
|
73
|
+
return results;
|
|
74
|
+
}
|
|
75
|
+
async function runCodemod(codemod, transformationPath, globItems, options) {
|
|
76
|
+
const { hooks, log: enableLogging, dry: runInDryMode } = defaultedOptions(options);
|
|
77
|
+
const extensions = new Set(Array.from(codemod.languages).reduce((acc, language)=>{
|
|
78
|
+
const mappedExtensions = LANG_TO_EXTENSIONS_MAPPING[language.toLowerCase()];
|
|
79
|
+
if (null == mappedExtensions) return acc;
|
|
80
|
+
return acc.concat(Array.from(mappedExtensions));
|
|
81
|
+
}, []));
|
|
82
|
+
const targets = globItems.filter((filepath)=>{
|
|
83
|
+
if (!hooks.targetFiltering(filepath)) return false;
|
|
84
|
+
const projectName = filepath.split('/')[0];
|
|
85
|
+
if (null == projectName) throw new Error('Invariant found, project name should be present');
|
|
86
|
+
return collectionIsEmpty(extensions) || extensions.has(external_node_path_default().extname(filepath));
|
|
87
|
+
});
|
|
88
|
+
if (0 === targets.length) return [];
|
|
89
|
+
if (enableLogging) console.log(`\u{1F9C9} '${codemod.name}' targeting ${targets.length} ${1 === targets.length ? 'file' : 'files'} to transform, chill and grab some mat\xe9`);
|
|
90
|
+
return Promise.all(targets.map(async (filepath)=>{
|
|
91
|
+
const fullPath = external_node_path_default().join(transformationPath, filepath);
|
|
92
|
+
try {
|
|
93
|
+
const content = await promises_default().readFile(fullPath, {
|
|
94
|
+
encoding: 'utf-8'
|
|
95
|
+
});
|
|
96
|
+
const modifications = await codemod.transformer(content, fullPath);
|
|
97
|
+
if (modifications.report.changesApplied > 0) {
|
|
98
|
+
const transformedContent = await hooks.postTransform(modifications.ast.root().text());
|
|
99
|
+
if (!runInDryMode) await promises_default().writeFile(fullPath, transformedContent);
|
|
100
|
+
if (enableLogging) console.log(`\u{1F680} finished '${codemod.name}'`, {
|
|
101
|
+
filename: filepath,
|
|
102
|
+
report: modifications.report
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return (0, external_neverthrow_namespaceObject.ok)(modifications);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (enableLogging) console.error(`\u{274C} '${codemod.name}' failed to parse file`, filepath, error);
|
|
108
|
+
return (0, external_neverthrow_namespaceObject.err)(error);
|
|
109
|
+
}
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
function defaultedOptions(options) {
|
|
113
|
+
return {
|
|
114
|
+
hooks: defaultedHooks(options?.hooks),
|
|
115
|
+
log: options?.log ?? true,
|
|
116
|
+
dry: options?.dry ?? false
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function defaultedHooks(hooks) {
|
|
120
|
+
const targetFiltering = hooks?.targetFiltering ?? (()=>true);
|
|
121
|
+
const postTransform = hooks?.postTransform ?? (async (content)=>content);
|
|
122
|
+
const preCodemodRun = hooks?.preCodemodRun ?? (async ()=>{});
|
|
123
|
+
return {
|
|
124
|
+
targetFiltering,
|
|
125
|
+
postTransform,
|
|
126
|
+
preCodemodRun
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
exports.runCodemod = __webpack_exports__.runCodemod;
|
|
130
|
+
exports.runCodemods = __webpack_exports__.runCodemods;
|
|
131
|
+
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
132
|
+
"runCodemod",
|
|
133
|
+
"runCodemods"
|
|
134
|
+
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
135
|
+
Object.defineProperty(exports, '__esModule', {
|
|
136
|
+
value: true
|
|
137
|
+
});
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { runCodemods, runCodemod, type Codemod } from './codemods';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import node_path from "node:path";
|
|
2
|
+
import promises from "node:fs/promises";
|
|
3
|
+
import fast_glob from "fast-glob";
|
|
4
|
+
import { err, ok } from "neverthrow";
|
|
5
|
+
import { Lang } from "@ast-grep/napi";
|
|
6
|
+
const LANG_TO_EXTENSIONS_MAPPING = {
|
|
7
|
+
[Lang.TypeScript.toLowerCase()]: new Set([
|
|
8
|
+
'.ts',
|
|
9
|
+
'.tsx',
|
|
10
|
+
'.js',
|
|
11
|
+
'.jsx',
|
|
12
|
+
'.cjs',
|
|
13
|
+
'.mjs',
|
|
14
|
+
'.mts'
|
|
15
|
+
])
|
|
16
|
+
};
|
|
17
|
+
function getCollectionCount(collection) {
|
|
18
|
+
if (Array.isArray(collection)) return collection.length;
|
|
19
|
+
return collection.size;
|
|
20
|
+
}
|
|
21
|
+
function collectionIsEmpty(collection) {
|
|
22
|
+
return 0 === getCollectionCount(collection);
|
|
23
|
+
}
|
|
24
|
+
async function runCodemods(codemods, transformationPath, options) {
|
|
25
|
+
const globItems = await fast_glob.glob([
|
|
26
|
+
'**/*'
|
|
27
|
+
], {
|
|
28
|
+
cwd: transformationPath
|
|
29
|
+
});
|
|
30
|
+
const results = {};
|
|
31
|
+
for (const codemod of codemods)results[codemod.name] = await runCodemod(codemod, transformationPath, globItems, options);
|
|
32
|
+
return results;
|
|
33
|
+
}
|
|
34
|
+
async function runCodemod(codemod, transformationPath, globItems, options) {
|
|
35
|
+
const { hooks, log: enableLogging, dry: runInDryMode } = defaultedOptions(options);
|
|
36
|
+
const extensions = new Set(Array.from(codemod.languages).reduce((acc, language)=>{
|
|
37
|
+
const mappedExtensions = LANG_TO_EXTENSIONS_MAPPING[language.toLowerCase()];
|
|
38
|
+
if (null == mappedExtensions) return acc;
|
|
39
|
+
return acc.concat(Array.from(mappedExtensions));
|
|
40
|
+
}, []));
|
|
41
|
+
const targets = globItems.filter((filepath)=>{
|
|
42
|
+
if (!hooks.targetFiltering(filepath)) return false;
|
|
43
|
+
const projectName = filepath.split('/')[0];
|
|
44
|
+
if (null == projectName) throw new Error('Invariant found, project name should be present');
|
|
45
|
+
return collectionIsEmpty(extensions) || extensions.has(node_path.extname(filepath));
|
|
46
|
+
});
|
|
47
|
+
if (0 === targets.length) return [];
|
|
48
|
+
if (enableLogging) console.log(`\u{1F9C9} '${codemod.name}' targeting ${targets.length} ${1 === targets.length ? 'file' : 'files'} to transform, chill and grab some mat\xe9`);
|
|
49
|
+
return Promise.all(targets.map(async (filepath)=>{
|
|
50
|
+
const fullPath = node_path.join(transformationPath, filepath);
|
|
51
|
+
try {
|
|
52
|
+
const content = await promises.readFile(fullPath, {
|
|
53
|
+
encoding: 'utf-8'
|
|
54
|
+
});
|
|
55
|
+
const modifications = await codemod.transformer(content, fullPath);
|
|
56
|
+
if (modifications.report.changesApplied > 0) {
|
|
57
|
+
const transformedContent = await hooks.postTransform(modifications.ast.root().text());
|
|
58
|
+
if (!runInDryMode) await promises.writeFile(fullPath, transformedContent);
|
|
59
|
+
if (enableLogging) console.log(`\u{1F680} finished '${codemod.name}'`, {
|
|
60
|
+
filename: filepath,
|
|
61
|
+
report: modifications.report
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return ok(modifications);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
if (enableLogging) console.error(`\u{274C} '${codemod.name}' failed to parse file`, filepath, error);
|
|
67
|
+
return err(error);
|
|
68
|
+
}
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
function defaultedOptions(options) {
|
|
72
|
+
return {
|
|
73
|
+
hooks: defaultedHooks(options?.hooks),
|
|
74
|
+
log: options?.log ?? true,
|
|
75
|
+
dry: options?.dry ?? false
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function defaultedHooks(hooks) {
|
|
79
|
+
const targetFiltering = hooks?.targetFiltering ?? (()=>true);
|
|
80
|
+
const postTransform = hooks?.postTransform ?? (async (content)=>content);
|
|
81
|
+
const preCodemodRun = hooks?.preCodemodRun ?? (async ()=>{});
|
|
82
|
+
return {
|
|
83
|
+
targetFiltering,
|
|
84
|
+
postTransform,
|
|
85
|
+
preCodemodRun
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
export { runCodemod, runCodemods };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type Optional<T> = T | undefined | null;
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kamaalio/codemod-kit",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"author": "Kamaal Farah",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"require": "./dist/index.cjs"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"main": "./dist/index.cjs",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@ast-grep/napi": "^0.38.5",
|
|
20
|
+
"fast-glob": "^3.3.3",
|
|
21
|
+
"neverthrow": "^8.2.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@eslint/js": "^9.29.0",
|
|
25
|
+
"@kamaalio/prettier-config": "^0.1.2",
|
|
26
|
+
"@rslib/core": "^0.10.2",
|
|
27
|
+
"@types/node": "^22.15.32",
|
|
28
|
+
"@vitest/coverage-v8": "3.2.4",
|
|
29
|
+
"eslint": "^9.29.0",
|
|
30
|
+
"globals": "^16.2.0",
|
|
31
|
+
"husky": "^9.1.7",
|
|
32
|
+
"lint-staged": "^16.1.2",
|
|
33
|
+
"prettier": "^3.5.3",
|
|
34
|
+
"tsx": "^4.20.3",
|
|
35
|
+
"typescript": "^5.8.3",
|
|
36
|
+
"typescript-eslint": "^8.34.1",
|
|
37
|
+
"vitest": "^3.2.4"
|
|
38
|
+
},
|
|
39
|
+
"lint-staged": {
|
|
40
|
+
"**/*": "prettier --write --ignore-unknown"
|
|
41
|
+
},
|
|
42
|
+
"prettier": "@kamaalio/prettier-config",
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "rslib build",
|
|
45
|
+
"build:clean": "rm -rf dist tsconfig.tsbuildinfo && pnpm run build",
|
|
46
|
+
"dev": "rslib build --watch",
|
|
47
|
+
"format": "prettier --write .",
|
|
48
|
+
"format:check": "prettier . --check",
|
|
49
|
+
"lint": "eslint .",
|
|
50
|
+
"test": "vitest run",
|
|
51
|
+
"test:cov": "vitest run --coverage",
|
|
52
|
+
"type-check": "tsc --noEmit"
|
|
53
|
+
}
|
|
54
|
+
}
|