@rtorcato/js-tooling 2.0.0 → 2.1.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/README.md CHANGED
@@ -67,6 +67,9 @@ npx @rtorcato/js-tooling copy tsconfig
67
67
  # List all available configurations
68
68
  npx @rtorcato/js-tooling list
69
69
 
70
+ # Diagnose drift between your project and our presets
71
+ npx @rtorcato/js-tooling doctor
72
+
70
73
  # Run commit message helper
71
74
  npx @rtorcato/js-tooling commitmessage
72
75
 
@@ -74,6 +77,18 @@ npx @rtorcato/js-tooling commitmessage
74
77
  npx @rtorcato/js-tooling helloworld
75
78
  ```
76
79
 
80
+ ### Doctor
81
+
82
+ `doctor` audits an existing project against the presets and reports drift:
83
+
84
+ ```bash
85
+ npx @rtorcato/js-tooling doctor # current dir
86
+ npx @rtorcato/js-tooling doctor -d ./app # specific dir
87
+ npx @rtorcato/js-tooling doctor --json # machine-readable output
88
+ ```
89
+
90
+ For each tracked config (TypeScript, Biome, ESLint, Prettier, Vitest, Commitlint, `package.json`) it reports `ok`, `drift`, `missing`, or `not configured`, and exits non-zero on `drift` or `missing` — handy as a CI check.
91
+
77
92
  ## Configuration Usage
78
93
 
79
94
  ### Biome (Formatter & Linter)
@@ -0,0 +1,160 @@
1
+ import chalk from 'chalk';
2
+ import fs from 'fs-extra';
3
+ import path from 'node:path';
4
+ const PACKAGE = '@rtorcato/js-tooling';
5
+ const FILE_CHECKS = [
6
+ {
7
+ check: 'TypeScript',
8
+ candidates: ['tsconfig.json'],
9
+ expected: `extends "${PACKAGE}/typescript/*"`,
10
+ matcher: /@rtorcato\/js-tooling\/typescript\//,
11
+ hint: 'Set `"extends": "@rtorcato/js-tooling/typescript/base"` in tsconfig.json',
12
+ },
13
+ {
14
+ check: 'Biome',
15
+ candidates: ['biome.json', 'biome.jsonc'],
16
+ expected: `extends "${PACKAGE}/biome"`,
17
+ matcher: /@rtorcato\/js-tooling\/biome/,
18
+ optional: true,
19
+ hint: 'Run `npx @rtorcato/js-tooling copy biome` to scaffold',
20
+ },
21
+ {
22
+ check: 'ESLint',
23
+ candidates: ['eslint.config.js', 'eslint.config.mjs', 'eslint.config.cjs'],
24
+ expected: `imports "${PACKAGE}/eslint/*"`,
25
+ matcher: /@rtorcato\/js-tooling\/eslint\//,
26
+ optional: true,
27
+ hint: 'Import from @rtorcato/js-tooling/eslint/base in eslint.config.mjs',
28
+ },
29
+ {
30
+ check: 'Prettier',
31
+ candidates: ['prettier.config.js', 'prettier.config.mjs', 'prettier.config.cjs'],
32
+ expected: `imports "${PACKAGE}/prettier"`,
33
+ matcher: /@rtorcato\/js-tooling\/prettier/,
34
+ optional: true,
35
+ },
36
+ {
37
+ check: 'Vitest',
38
+ candidates: ['vitest.config.ts', 'vitest.config.js', 'vitest.config.mjs'],
39
+ expected: `imports "${PACKAGE}/vitest/config"`,
40
+ matcher: /@rtorcato\/js-tooling\/vitest\/config/,
41
+ optional: true,
42
+ },
43
+ {
44
+ check: 'Commitlint',
45
+ candidates: ['commitlint.config.js', 'commitlint.config.mjs', 'commitlint.config.cjs'],
46
+ expected: `exports "${PACKAGE}/commitlint/config"`,
47
+ matcher: /@rtorcato\/js-tooling\/commitlint\/config/,
48
+ optional: true,
49
+ },
50
+ ];
51
+ async function checkFile(dir, spec) {
52
+ for (const candidate of spec.candidates) {
53
+ const filepath = path.join(dir, candidate);
54
+ if (!(await fs.pathExists(filepath)))
55
+ continue;
56
+ const contents = await fs.readFile(filepath, 'utf-8');
57
+ if (spec.matcher.test(contents)) {
58
+ return {
59
+ check: spec.check,
60
+ status: 'ok',
61
+ detail: `${candidate} ${spec.expected}`,
62
+ };
63
+ }
64
+ return {
65
+ check: spec.check,
66
+ status: 'drift',
67
+ detail: `${candidate} found but does not ${spec.expected}`,
68
+ hint: spec.hint,
69
+ };
70
+ }
71
+ return {
72
+ check: spec.check,
73
+ status: spec.optional ? 'optional-missing' : 'missing',
74
+ detail: `no ${spec.candidates.join(' / ')} found`,
75
+ hint: spec.hint,
76
+ };
77
+ }
78
+ async function checkPackageJson(dir) {
79
+ const filepath = path.join(dir, 'package.json');
80
+ if (!(await fs.pathExists(filepath))) {
81
+ return {
82
+ check: 'package.json',
83
+ status: 'missing',
84
+ detail: 'no package.json found',
85
+ };
86
+ }
87
+ const pkg = await fs.readJson(filepath);
88
+ const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };
89
+ if (deps[PACKAGE]) {
90
+ return {
91
+ check: 'package.json',
92
+ status: 'ok',
93
+ detail: `${PACKAGE}@${deps[PACKAGE]} in dependencies`,
94
+ };
95
+ }
96
+ return {
97
+ check: 'package.json',
98
+ status: 'drift',
99
+ detail: `${PACKAGE} not in dependencies or devDependencies`,
100
+ hint: `Run \`pnpm add -D ${PACKAGE}\``,
101
+ };
102
+ }
103
+ export async function runDoctor(dir) {
104
+ const targetDir = path.resolve(dir);
105
+ const results = [];
106
+ results.push(await checkPackageJson(targetDir));
107
+ for (const spec of FILE_CHECKS) {
108
+ results.push(await checkFile(targetDir, spec));
109
+ }
110
+ return results;
111
+ }
112
+ const STATUS_ICONS = {
113
+ ok: chalk.green('✅'),
114
+ drift: chalk.yellow('⚠️ '),
115
+ missing: chalk.red('❌'),
116
+ 'optional-missing': chalk.gray('➖'),
117
+ };
118
+ function statusLabel(status) {
119
+ switch (status) {
120
+ case 'ok':
121
+ return chalk.green('ok');
122
+ case 'drift':
123
+ return chalk.yellow('drift');
124
+ case 'missing':
125
+ return chalk.red('missing');
126
+ case 'optional-missing':
127
+ return chalk.gray('not configured');
128
+ }
129
+ }
130
+ export function summarize(results) {
131
+ return {
132
+ ok: results.filter((r) => r.status === 'ok').length,
133
+ drift: results.filter((r) => r.status === 'drift').length,
134
+ missing: results.filter((r) => r.status === 'missing').length,
135
+ optionalMissing: results.filter((r) => r.status === 'optional-missing').length,
136
+ };
137
+ }
138
+ export async function doctorCommand(options = {}) {
139
+ const dir = options.directory ?? process.cwd();
140
+ const results = await runDoctor(dir);
141
+ if (options.json) {
142
+ console.log(JSON.stringify({ directory: path.resolve(dir), results }, null, 2));
143
+ }
144
+ else {
145
+ console.log(chalk.cyan(`\n🩺 Diagnosing ${path.resolve(dir)} against ${PACKAGE} presets...\n`));
146
+ for (const r of results) {
147
+ console.log(` ${STATUS_ICONS[r.status]} ${chalk.bold(r.check)} — ${statusLabel(r.status)}`);
148
+ console.log(` ${chalk.gray(r.detail)}`);
149
+ if (r.hint && (r.status === 'drift' || r.status === 'missing')) {
150
+ console.log(` ${chalk.dim('hint:')} ${chalk.dim(r.hint)}`);
151
+ }
152
+ }
153
+ const summary = summarize(results);
154
+ console.log();
155
+ console.log(` Summary: ${chalk.green(`${summary.ok} ok`)}, ${chalk.yellow(`${summary.drift} drift`)}, ${chalk.red(`${summary.missing} missing`)}, ${chalk.gray(`${summary.optionalMissing} not configured`)}\n`);
156
+ }
157
+ const summary = summarize(results);
158
+ const exitCode = summary.drift > 0 || summary.missing > 0 ? 1 : 0;
159
+ process.exitCode = exitCode;
160
+ }
package/dist/cli/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import chalk from 'chalk';
3
3
  import { Command } from 'commander';
4
4
  import packageJson from '../../package.json' with { type: 'json' };
5
+ import { doctorCommand } from './commands/doctor.js';
5
6
  import { setupProject } from './commands/setup.js';
6
7
  const program = new Command();
7
8
  program
@@ -89,6 +90,12 @@ program
89
90
  });
90
91
  console.log(chalk.dim('\n💡 Run "js-tooling setup" to configure your project\n'));
91
92
  });
93
+ program
94
+ .command('doctor')
95
+ .description('🩺 Diagnose project alignment with @rtorcato/js-tooling presets')
96
+ .option('-d, --directory <path>', 'Target directory to diagnose', process.cwd())
97
+ .option('--json', 'Emit machine-readable JSON output')
98
+ .action(doctorCommand);
92
99
  // Handle unknown commands
93
100
  program.on('command:*', () => {
94
101
  console.error(chalk.red(`\n❌ Unknown command: ${program.args.join(' ')}`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rtorcato/js-tooling",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "JavaScript and TypeScript tooling for Node.js, React, Next.js, and Vitest.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -20,8 +20,7 @@
20
20
  "url": "https://github.com/rtorcato/js-tooling/issues"
21
21
  },
22
22
  "scripts": {
23
- "build-dev": "rimraf ./dist && cross-env NODE_ENV=development node build.mjs && pnpm build-cli",
24
- "build-prod": "rimraf ./dist && cross-env NODE_ENV=production node build.mjs && pnpm build-cli",
23
+ "build": "pnpm build-cli",
25
24
  "build-cli": "rimraf ./dist/cli && tsc --project src/cli/tsconfig.json",
26
25
  "prepublishOnly": "./scripts/fix-bins.sh",
27
26
  "==================== Common ====================": "",
@@ -39,31 +38,56 @@
39
38
  },
40
39
  "files": [
41
40
  "dist/cli/**/*.js",
42
- "tooling/*",
43
41
  "tooling/commitlint/commitlint.mjs",
42
+ "tooling/commitlint/commitlint.d.mts",
44
43
  "tooling/esbuild/index.mjs",
44
+ "tooling/esbuild/index.d.mts",
45
45
  "tooling/eslint/*.mjs",
46
+ "tooling/eslint/*.d.mts",
47
+ "tooling/eslint/types.d.ts",
46
48
  "tooling/jest-presets/**",
47
49
  "tooling/prettier/index.mjs",
50
+ "tooling/prettier/index.d.mts",
48
51
  "tooling/typescript/*.json",
49
52
  "tooling/typescript/reset.d.ts",
50
53
  "tooling/vitest/vitest.config.mjs",
54
+ "tooling/vitest/vitest.config.d.mts",
51
55
  "tooling/tsup/index.ts",
52
56
  "tooling/biome/biome.json",
53
57
  "tooling/biome/biome.jsonc",
54
58
  "tooling/semantic-release/*.mjs",
59
+ "tooling/semantic-release/*.d.mts",
55
60
  "README.md"
56
61
  ],
57
62
  "exports": {
58
63
  "./commitlint/config": {
64
+ "types": "./tooling/commitlint/commitlint.d.mts",
59
65
  "import": "./tooling/commitlint/commitlint.mjs"
60
66
  },
61
- "./esbuild": "./tooling/esbuild/index.mjs",
62
- "./eslint/base": "./tooling/eslint/base.mjs",
63
- "./eslint/nextjs": "./tooling/eslint/nextjs.mjs",
64
- "./jest-presets/browser/jest-preset": "./tooling/jest-presets/browser/jest-preset.mjs",
65
- "./jest-presets/node/jest-preset": "./tooling/jest-presets/node/jest-preset.mjs",
66
- "./prettier": "./tooling/prettier/index.mjs",
67
+ "./esbuild": {
68
+ "types": "./tooling/esbuild/index.d.mts",
69
+ "import": "./tooling/esbuild/index.mjs"
70
+ },
71
+ "./eslint/base": {
72
+ "types": "./tooling/eslint/base.d.mts",
73
+ "import": "./tooling/eslint/base.mjs"
74
+ },
75
+ "./eslint/nextjs": {
76
+ "types": "./tooling/eslint/nextjs.d.mts",
77
+ "import": "./tooling/eslint/nextjs.mjs"
78
+ },
79
+ "./jest-presets/browser/jest-preset": {
80
+ "types": "./tooling/jest-presets/browser/jest-preset.d.mts",
81
+ "import": "./tooling/jest-presets/browser/jest-preset.mjs"
82
+ },
83
+ "./jest-presets/node/jest-preset": {
84
+ "types": "./tooling/jest-presets/node/jest-preset.d.mts",
85
+ "import": "./tooling/jest-presets/node/jest-preset.mjs"
86
+ },
87
+ "./prettier": {
88
+ "types": "./tooling/prettier/index.d.mts",
89
+ "import": "./tooling/prettier/index.mjs"
90
+ },
67
91
  "./typescript/base": "./tooling/typescript/tsconfig.base.json",
68
92
  "./typescript/next": "./tooling/typescript/tsconfig.next.json",
69
93
  "./typescript/express": "./tooling/typescript/tsconfig.express.json",
@@ -71,12 +95,24 @@
71
95
  "./typescript/react": "./tooling/typescript/tsconfig.react.json",
72
96
  "./typescript/test": "./tooling/typescript/tsconfig.test.json",
73
97
  "./typescript/reset": "./tooling/typescript/reset.d.ts",
74
- "./vitest/config": "./tooling/vitest/vitest.config.mjs",
98
+ "./vitest/config": {
99
+ "types": "./tooling/vitest/vitest.config.d.mts",
100
+ "import": "./tooling/vitest/vitest.config.mjs"
101
+ },
75
102
  "./tsup": "./tooling/tsup/index.ts",
76
103
  "./biome": "./tooling/biome/biome.json",
77
- "./semantic-release": "./tooling/semantic-release/index.mjs",
78
- "./semantic-release/github": "./tooling/semantic-release/github.mjs",
79
- "./semantic-release/docker": "./tooling/semantic-release/docker.mjs"
104
+ "./semantic-release": {
105
+ "types": "./tooling/semantic-release/index.d.mts",
106
+ "import": "./tooling/semantic-release/index.mjs"
107
+ },
108
+ "./semantic-release/github": {
109
+ "types": "./tooling/semantic-release/github.d.mts",
110
+ "import": "./tooling/semantic-release/github.mjs"
111
+ },
112
+ "./semantic-release/docker": {
113
+ "types": "./tooling/semantic-release/docker.d.mts",
114
+ "import": "./tooling/semantic-release/docker.mjs"
115
+ }
80
116
  },
81
117
  "dependencies": {
82
118
  "chalk": "^5.6.2",
@@ -104,6 +140,7 @@
104
140
  "@typescript-eslint/eslint-plugin": "^8.46.2",
105
141
  "@typescript-eslint/parser": "^8.46.2",
106
142
  "@vitejs/plugin-react": "^5.1.0",
143
+ "@vitest/coverage-v8": "^4.0.3",
107
144
  "commitizen": "^4.3.1",
108
145
  "conventional-changelog-conventionalcommits": "^9.1.0",
109
146
  "cross-env": "^10.1.0",
@@ -130,7 +167,7 @@
130
167
  "ts-jest": "^29.4.5",
131
168
  "tsup": "8.5.0",
132
169
  "typescript": "^5.9.3",
133
- "typescript-eslint": "latest",
170
+ "typescript-eslint": "^8.60.0",
134
171
  "vitest": "4.0.3"
135
172
  },
136
173
  "peerDependencies": {
@@ -0,0 +1,4 @@
1
+ import type { UserConfig } from '@commitlint/types'
2
+
3
+ declare const config: UserConfig
4
+ export default config
@@ -0,0 +1,6 @@
1
+ import type { BuildOptions, BuildResult } from 'esbuild'
2
+
3
+ export declare function buildCode(
4
+ entryPoints?: string[],
5
+ options?: Partial<BuildOptions>
6
+ ): Promise<BuildResult | undefined>
@@ -0,0 +1,6 @@
1
+ import type { Linter } from 'eslint'
2
+
3
+ declare const config: Linter.Config[]
4
+ export default config
5
+
6
+ export declare const restrictEnvAccess: Linter.Config[]
@@ -0,0 +1,4 @@
1
+ import type { Linter } from 'eslint'
2
+
3
+ declare const config: Linter.Config[]
4
+ export default config
@@ -0,0 +1,4 @@
1
+ import type { Config } from 'jest'
2
+
3
+ declare const config: Config
4
+ export default config
@@ -0,0 +1,4 @@
1
+ import type { Config } from 'jest'
2
+
3
+ declare const config: Config
4
+ export default config
@@ -0,0 +1,4 @@
1
+ import type { Config } from 'prettier'
2
+
3
+ declare const config: Config
4
+ export default config
@@ -0,0 +1,4 @@
1
+ import type { Options } from 'semantic-release'
2
+
3
+ declare const config: Options
4
+ export default config
@@ -0,0 +1,4 @@
1
+ import type { Options } from 'semantic-release'
2
+
3
+ declare const config: Options
4
+ export default config
@@ -0,0 +1,4 @@
1
+ import type { Options } from 'semantic-release'
2
+
3
+ declare const config: Options
4
+ export default config
@@ -0,0 +1,4 @@
1
+ import type { UserConfig } from 'vitest/config'
2
+
3
+ declare const config: UserConfig
4
+ export default config
@@ -9,8 +9,15 @@ export default defineConfig({
9
9
  globals: true,
10
10
  environment: 'node',
11
11
  coverage: {
12
- provider: 'istanbul', // or 'v8'
12
+ provider: 'v8',
13
13
  reporter: ['text', 'json', 'html'],
14
+ include: ['src/cli/generators/**/*.ts'],
15
+ thresholds: {
16
+ statements: 25,
17
+ lines: 25,
18
+ functions: 40,
19
+ branches: 17,
20
+ },
14
21
  },
15
22
  },
16
23
  resolve: {
@@ -1,21 +0,0 @@
1
- /*
2
- Sample call to build.mjs in root of project.
3
-
4
- */
5
-
6
- import { buildCode, getEntrypointFolders } from './index.mjs'
7
- // import { buildCode, getEntrypointFolders } from '@rtorcato/js-tooling/esbuild/index.mjs'
8
-
9
- const folders = await getEntrypointFolders('src')
10
- const libEntryPointsArrays = await Promise.all(folders.map((folder) => getEntryPoints(folder)))
11
- const libEntryPoints = libEntryPointsArrays.flat()
12
- const allEntryPoints = [
13
- 'src/index.ts', // Main entry point
14
- ...libEntryPoints,
15
- // ...exampleEntryPoints,
16
- ]
17
- // Run the build function
18
- buildCode(allEntryPoints).catch((e) => {
19
- console.error(e)
20
- process.exit(1)
21
- })
@@ -1,35 +0,0 @@
1
- {
2
- "name": "@dropwallet/eslint-config",
3
- "private": true,
4
- "version": "0.3.0",
5
- "type": "module",
6
- "exports": {
7
- "./base": "./base.js",
8
- "./nextjs": "./nextjs.js"
9
- },
10
- "scripts": {
11
- "clean": "rm -rf .turbo node_modules",
12
- "format": "prettier --check . --ignore-path ../../.gitignore",
13
- "typecheck": "tsc --noEmit"
14
- },
15
- "dependencies": {
16
- "@next/eslint-plugin-next": "^14.2.3",
17
- "eslint-config-airbnb": "^19.0.4",
18
- "eslint-config-prettier": "9.1.0",
19
- "eslint-config-turbo": "^1.13.3",
20
- "eslint-plugin-import": "^2.29.1",
21
- "eslint-plugin-jest": "28.5.0",
22
- "eslint-plugin-jsx-a11y": "^6.8.0",
23
- "eslint-plugin-react": "^7.34.1",
24
- "eslint-plugin-react-hooks": "^4.6.2",
25
- "eslint-plugin-vitest": "0.5.4",
26
- "typescript-eslint": "7.9.0"
27
- },
28
- "devDependencies": {
29
- "@dropwallet/tsconfig": "workspace:*",
30
- "@eslint/js": "9.2.0",
31
- "eslint": "9.2.0",
32
- "prettier": "^3.2.5",
33
- "typescript": "^5.4.5"
34
- }
35
- }
@@ -1,4 +0,0 @@
1
- /** @type {import('next').NextConfig} */
2
- module.exports = {
3
- output: 'standalone',
4
- }
File without changes
@@ -1 +0,0 @@
1
- https://oxc.rs
File without changes
File without changes
File without changes
File without changes
@@ -1,7 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "./dist",
5
- "module": "commonjs"
6
- }
7
- }
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "./dist",
5
- "module": "esnext",
6
- "emitDeclarationOnly": false,
7
- "noEmit": false
8
- }
9
- }
@@ -1,11 +0,0 @@
1
- {
2
- "extends": "./../typescript/internal-package.json",
3
- "compilerOptions": {
4
- "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json",
5
- "incremental": true,
6
- "outDir": "./dist",
7
- "baseUrl": "."
8
- },
9
- "include": ["./src/**/*.ts", "index.ts"],
10
- "exclude": ["node_modules"]
11
- }
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "./dist",
5
- "declaration": true,
6
- "noEmit": false,
7
- "emitDeclarationOnly": true
8
- }
9
- }
File without changes
File without changes
@@ -1,27 +0,0 @@
1
- import base from '@rtorcato/js-tooling/vitest/config'
2
- import react from '@vitejs/plugin-react'
3
- import { dirname, resolve } from 'node:path'
4
- import { fileURLToPath } from 'node:url'
5
- import { defineConfig, mergeConfig } from 'vitest/config'
6
-
7
- const __dirname = dirname(fileURLToPath(import.meta.url))
8
-
9
- export default mergeConfig(
10
- base,
11
- defineConfig({
12
- plugins: [react()],
13
- test: {
14
- environment: 'jsdom',
15
- include: ['src/**/*.{test,spec}.{js,jsx,ts,tsx}'],
16
- css: true, // ← Vitest will stub every *.css / *.module.css import
17
- exclude: ['OLD/**'],
18
- setupFiles: ['./vitest.setup.ts'],
19
- },
20
- resolve: {
21
- alias: [
22
- { find: '@', replacement: resolve(__dirname, './src') },
23
- { find: '~', replacement: resolve(__dirname, './src') },
24
- ],
25
- },
26
- })
27
- )
@@ -1,3 +0,0 @@
1
- import { vi } from 'vitest'
2
-
3
- vi.mock('**/*.module.css', () => ({}))