@rtorcato/js-tooling 2.19.0 → 2.19.2

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.
@@ -210,17 +210,17 @@ const FIXERS = [
210
210
  },
211
211
  {
212
212
  target: 'semantic-release',
213
- description: 'Scaffold release.config.mjs (skipped on private packages)',
213
+ description: 'Scaffold release.config.mjs + install preset plugins (skipped on private packages)',
214
214
  appliesTo: ['semantic-release'],
215
- outputs: ['release.config.mjs'],
215
+ outputs: ['release.config.mjs', 'package.json'],
216
216
  canFixDrift: true,
217
217
  async run({ targetDir, pkg }) {
218
218
  if (pkg?.private === true) {
219
219
  console.log(chalk.gray(' skipping — package is private'));
220
220
  return { filesWritten: [] };
221
221
  }
222
- await generateSemanticReleaseConfig(targetDir);
223
- return { filesWritten: ['release.config.mjs'] };
222
+ const filesWritten = await generateSemanticReleaseConfig(targetDir);
223
+ return { filesWritten };
224
224
  },
225
225
  },
226
226
  {
@@ -71,11 +71,41 @@ export default mergeConfig(preset, defineConfig({ plugins: [react()] }))
71
71
  `;
72
72
  await fs.writeFile(viteConfigPath, viteConfig);
73
73
  }
74
+ // Plugins the github/gitlab preset activates that semantic-release core does
75
+ // NOT bundle (core bundles only commit-analyzer, release-notes-generator, npm,
76
+ // github). Without these in the consumer's deps, `semantic-release` crashes
77
+ // with "Cannot find module '@semantic-release/changelog'" on first run.
78
+ const RELEASE_PLUGIN_DEPS = {
79
+ '@semantic-release/changelog': '^6.0.0',
80
+ '@semantic-release/git': '^10.0.0',
81
+ };
74
82
  export async function generateSemanticReleaseConfig(targetDir) {
75
83
  const releaseConfigPath = path.join(targetDir, 'release.config.mjs');
76
84
  const releaseConfig = `export { default } from '@rtorcato/js-tooling/semantic-release/github'
77
85
  `;
78
86
  await fs.writeFile(releaseConfigPath, releaseConfig);
87
+ const written = ['release.config.mjs'];
88
+ // Ensure the preset's non-bundled plugins are installed; otherwise the
89
+ // scaffolded release.config.mjs references modules the consumer lacks.
90
+ const pkgPath = path.join(targetDir, 'package.json');
91
+ if (await fs.pathExists(pkgPath)) {
92
+ const pkg = (await fs.readJson(pkgPath));
93
+ const devDeps = (pkg.devDependencies ?? {});
94
+ const deps = (pkg.dependencies ?? {});
95
+ let changed = false;
96
+ for (const [name, version] of Object.entries(RELEASE_PLUGIN_DEPS)) {
97
+ if (!devDeps[name] && !deps[name]) {
98
+ devDeps[name] = version;
99
+ changed = true;
100
+ }
101
+ }
102
+ if (changed) {
103
+ pkg.devDependencies = devDeps;
104
+ await fs.writeJson(pkgPath, pkg, { spaces: 2 });
105
+ written.push('package.json');
106
+ }
107
+ }
108
+ return written;
79
109
  }
80
110
  export async function generateChangesetsConfig(targetDir) {
81
111
  // Drop the canonical Changesets config into .changeset/config.json. The user
@@ -57,19 +57,15 @@ npx --no -- commitlint --edit $1
57
57
  // lint-staged configuration in package.json
58
58
  const packageJsonPath = path.join(targetDir, 'package.json');
59
59
  const packageJson = await fs.readJson(packageJsonPath);
60
+ // No explicit `git add` — lint-staged stages tool output itself, and the
61
+ // extra add races its index lock. `--no-errors-on-unmatched` keeps biome
62
+ // from failing a commit when every matched file is biome-ignored.
63
+ const useBiome = config.linting.tool === 'biome' || config.linting.tool === 'both';
60
64
  packageJson['lint-staged'] = {
61
- '*.{js,ts,jsx,tsx}': [
62
- config.linting.tool === 'biome' || config.linting.tool === 'both'
63
- ? 'biome check --fix'
64
- : 'eslint --fix',
65
- 'git add',
66
- ],
67
- '*.{json,md,yml,yaml}': [
68
- config.linting.tool === 'biome' || config.linting.tool === 'both'
69
- ? 'biome format --write'
70
- : 'prettier --write',
71
- 'git add',
72
- ],
65
+ '*.{js,ts,jsx,tsx}': useBiome ? 'biome check --fix --no-errors-on-unmatched' : 'eslint --fix',
66
+ '*.{json,md,yml,yaml}': useBiome
67
+ ? 'biome format --write --no-errors-on-unmatched'
68
+ : 'prettier --write',
73
69
  };
74
70
  await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
75
71
  }
@@ -48,15 +48,15 @@ jobs:
48
48
  cache-key: \${{ steps.cache-key.outputs.key }}
49
49
  steps:
50
50
  - name: 📦 Checkout repository
51
- uses: actions/checkout@v4
51
+ uses: actions/checkout@v7
52
52
 
53
53
  - name: 📦 Setup Node.js
54
- uses: actions/setup-node@v4
54
+ uses: actions/setup-node@v6
55
55
  with:
56
- node-version: '20'
56
+ node-version-file: .nvmrc
57
57
 
58
58
  - name: 📦 Setup pnpm
59
- uses: pnpm/action-setup@v4
59
+ uses: pnpm/action-setup@v6
60
60
  with:
61
61
  version: latest
62
62
 
@@ -65,7 +65,7 @@ jobs:
65
65
  run: echo "key=\${{ runner.os }}-pnpm-\${{ hashFiles('**/pnpm-lock.yaml') }}" >> $GITHUB_OUTPUT
66
66
 
67
67
  - name: 📦 Cache dependencies
68
- uses: actions/cache@v4
68
+ uses: actions/cache@v5
69
69
  with:
70
70
  path: |
71
71
  ~/.pnpm-store
@@ -83,20 +83,20 @@ jobs:
83
83
  if: needs.check-skip.outputs.should-skip != 'true'
84
84
  steps:
85
85
  - name: 📦 Checkout repository
86
- uses: actions/checkout@v4
86
+ uses: actions/checkout@v7
87
87
 
88
88
  - name: 📦 Setup Node.js
89
- uses: actions/setup-node@v4
89
+ uses: actions/setup-node@v6
90
90
  with:
91
- node-version: '20'
91
+ node-version-file: .nvmrc
92
92
 
93
93
  - name: 📦 Setup pnpm
94
- uses: pnpm/action-setup@v4
94
+ uses: pnpm/action-setup@v6
95
95
  with:
96
96
  version: latest
97
97
 
98
98
  - name: 📦 Restore dependencies cache
99
- uses: actions/cache@v4
99
+ uses: actions/cache@v5
100
100
  with:
101
101
  path: |
102
102
  ~/.pnpm-store
@@ -113,20 +113,20 @@ ${hasTypeScript
113
113
  if: needs.check-skip.outputs.should-skip != 'true'
114
114
  steps:
115
115
  - name: 📦 Checkout repository
116
- uses: actions/checkout@v4
116
+ uses: actions/checkout@v7
117
117
 
118
118
  - name: 📦 Setup Node.js
119
- uses: actions/setup-node@v4
119
+ uses: actions/setup-node@v6
120
120
  with:
121
- node-version: '20'
121
+ node-version-file: .nvmrc
122
122
 
123
123
  - name: 📦 Setup pnpm
124
- uses: pnpm/action-setup@v4
124
+ uses: pnpm/action-setup@v6
125
125
  with:
126
126
  version: latest
127
127
 
128
128
  - name: 📦 Restore dependencies cache
129
- uses: actions/cache@v4
129
+ uses: actions/cache@v5
130
130
  with:
131
131
  path: |
132
132
  ~/.pnpm-store
@@ -144,20 +144,20 @@ ${hasTests
144
144
  if: needs.check-skip.outputs.should-skip != 'true'
145
145
  steps:
146
146
  - name: 📦 Checkout repository
147
- uses: actions/checkout@v4
147
+ uses: actions/checkout@v7
148
148
 
149
149
  - name: 📦 Setup Node.js
150
- uses: actions/setup-node@v4
150
+ uses: actions/setup-node@v6
151
151
  with:
152
- node-version: '20'
152
+ node-version-file: .nvmrc
153
153
 
154
154
  - name: 📦 Setup pnpm
155
- uses: pnpm/action-setup@v4
155
+ uses: pnpm/action-setup@v6
156
156
  with:
157
157
  version: latest
158
158
 
159
159
  - name: 📦 Restore dependencies cache
160
- uses: actions/cache@v4
160
+ uses: actions/cache@v5
161
161
  with:
162
162
  path: |
163
163
  ~/.pnpm-store
@@ -175,20 +175,20 @@ ${hasBuild
175
175
  if: needs.check-skip.outputs.should-skip != 'true'
176
176
  steps:
177
177
  - name: 📦 Checkout repository
178
- uses: actions/checkout@v4
178
+ uses: actions/checkout@v7
179
179
 
180
180
  - name: 📦 Setup Node.js
181
- uses: actions/setup-node@v4
181
+ uses: actions/setup-node@v6
182
182
  with:
183
- node-version: '20'
183
+ node-version-file: .nvmrc
184
184
 
185
185
  - name: 📦 Setup pnpm
186
- uses: pnpm/action-setup@v4
186
+ uses: pnpm/action-setup@v6
187
187
  with:
188
188
  version: latest
189
189
 
190
190
  - name: 📦 Restore dependencies cache
191
- uses: actions/cache@v4
191
+ uses: actions/cache@v5
192
192
  with:
193
193
  path: |
194
194
  ~/.pnpm-store
@@ -199,7 +199,7 @@ ${hasBuild
199
199
  run: pnpm build
200
200
 
201
201
  - name: 📦 Upload build artifacts
202
- uses: actions/upload-artifact@v4
202
+ uses: actions/upload-artifact@v7
203
203
  with:
204
204
  name: build-artifacts
205
205
  path: |
@@ -221,24 +221,24 @@ ${isLibrary && config.semanticRelease
221
221
  id-token: write
222
222
  steps:
223
223
  - name: 📦 Checkout repository
224
- uses: actions/checkout@v4
224
+ uses: actions/checkout@v7
225
225
  with:
226
226
  fetch-depth: 0
227
227
  token: \${{ secrets.GITHUB_TOKEN }}
228
228
 
229
229
  - name: 📦 Setup Node.js
230
- uses: actions/setup-node@v4
230
+ uses: actions/setup-node@v6
231
231
  with:
232
- node-version: '20'
232
+ node-version-file: .nvmrc
233
233
  registry-url: 'https://registry.npmjs.org'
234
234
 
235
235
  - name: 📦 Setup pnpm
236
- uses: pnpm/action-setup@v4
236
+ uses: pnpm/action-setup@v6
237
237
  with:
238
238
  version: latest
239
239
 
240
240
  - name: 📦 Restore dependencies cache
241
- uses: actions/cache@v4
241
+ uses: actions/cache@v5
242
242
  with:
243
243
  path: |
244
244
  ~/.pnpm-store
@@ -59,7 +59,7 @@ function renderGitLabCI(config) {
59
59
  return `# .gitlab-ci.yml — generated by @rtorcato/js-tooling
60
60
  # Customize stages and jobs to fit your pipeline.
61
61
 
62
- image: node:20
62
+ image: node:22
63
63
 
64
64
  stages:
65
65
  ${stages.map((s) => ` - ${s}`).join('\n')}
@@ -27,13 +27,13 @@ export async function generateOxlintConfig(targetDir) {
27
27
  }
28
28
  export async function generateBiomeConfig(targetDir) {
29
29
  const biomeConfigPath = path.join(targetDir, 'biome.jsonc');
30
+ // Biome 2.x schema + shape. The base preset (extends) already defines the
31
+ // file globs via `files.includes`; emitting the old 1.x `include`/`ignore`
32
+ // keys here forced consumers to run `biome migrate` before `biome check`
33
+ // would run at all.
30
34
  const biomeConfig = {
31
- $schema: 'https://biomejs.dev/schemas/1.9.4/schema.json',
35
+ $schema: 'https://biomejs.dev/schemas/2.5.0/schema.json',
32
36
  extends: ['@rtorcato/js-tooling/biome'],
33
- files: {
34
- include: ['src/**/*', '*.ts', '*.js', '*.tsx', '*.jsx'],
35
- ignore: ['node_modules', 'dist', 'build', '.next'],
36
- },
37
37
  };
38
38
  await fs.writeJson(biomeConfigPath, biomeConfig, { spaces: 2 });
39
39
  }
@@ -26,16 +26,22 @@ export async function generatePackageJson(config, targetDir) {
26
26
  ...existingPackageJson?.devDependencies,
27
27
  },
28
28
  };
29
- // Add additional package.json fields based on project type
29
+ // Add additional package.json fields based on project type.
30
+ // Exports must match tsup's output for a "type": "module" package with
31
+ // format: ['cjs','esm']: ESM → index.js, CJS → index.cjs, types →
32
+ // index.d.ts (ESM) / index.d.cts (CJS).
30
33
  if (config.projectType === 'library') {
31
- packageJson.main = './dist/index.js';
32
- packageJson.module = './dist/index.mjs';
34
+ packageJson.main = './dist/index.cjs';
35
+ packageJson.module = './dist/index.js';
33
36
  packageJson.types = './dist/index.d.ts';
34
37
  packageJson.exports = {
35
38
  '.': {
36
- import: './dist/index.mjs',
37
- require: './dist/index.js',
38
- types: './dist/index.d.ts',
39
+ types: {
40
+ import: './dist/index.d.ts',
41
+ require: './dist/index.d.cts',
42
+ },
43
+ import: './dist/index.js',
44
+ require: './dist/index.cjs',
39
45
  },
40
46
  };
41
47
  packageJson.files = ['dist'];
@@ -43,6 +49,15 @@ export async function generatePackageJson(config, targetDir) {
43
49
  access: 'public',
44
50
  };
45
51
  }
52
+ // pnpm 11 refuses to run a dependency's build script unless it's approved.
53
+ // esbuild (pulled in by tsup/esbuild/vite) has one, so `pnpm install` exits
54
+ // 1 with ERR_PNPM_IGNORED_BUILDS until it's whitelisted here.
55
+ if (config.bundler === 'tsup' || config.bundler === 'esbuild' || config.bundler === 'vite') {
56
+ packageJson.pnpm = {
57
+ ...packageJson.pnpm,
58
+ onlyBuiltDependencies: ['esbuild'],
59
+ };
60
+ }
46
61
  await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
47
62
  }
48
63
  function getScripts(config, opts = {}) {
@@ -213,10 +228,14 @@ function getDependencies(config) {
213
228
  deps['@commitlint/cli'] = '^20.0.0';
214
229
  deps['@commitlint/config-conventional'] = '^20.0.0';
215
230
  }
216
- // Semantic release
231
+ // Semantic release. The shipped github preset activates the changelog and
232
+ // git plugins (and @semantic-release/github), so they must be installed too
233
+ // or `semantic-release` crashes with "Cannot find module".
217
234
  if (config.semanticRelease) {
218
235
  deps['semantic-release'] = '^25.0.0';
219
236
  deps['@semantic-release/github'] = '^12.0.0';
237
+ deps['@semantic-release/changelog'] = '^6.0.0';
238
+ deps['@semantic-release/git'] = '^10.0.0';
220
239
  }
221
240
  return deps;
222
241
  }
@@ -75,7 +75,7 @@ jobs:
75
75
 
76
76
  steps:
77
77
  - name: Checkout
78
- uses: actions/checkout@v4
78
+ uses: actions/checkout@v7
79
79
 
80
80
  - name: Initialize CodeQL
81
81
  uses: github/codeql-action/init@v3
@@ -10,9 +10,9 @@ jobs:
10
10
  permissions:
11
11
  contents: write
12
12
  steps:
13
- - uses: actions/checkout@v4
14
- - uses: pnpm/action-setup@v4
15
- - uses: actions/setup-node@v4
13
+ - uses: actions/checkout@v7
14
+ - uses: pnpm/action-setup@v6
15
+ - uses: actions/setup-node@v6
16
16
  with:
17
17
  node-version: 22
18
18
  cache: pnpm
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rtorcato/js-tooling",
3
- "version": "2.19.0",
3
+ "version": "2.19.2",
4
4
  "description": "JavaScript and TypeScript tooling for Node.js, React, Next.js, and Vitest.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -71,7 +71,8 @@
71
71
  "tooling/tests/exports-resolution.d.mts",
72
72
  "tooling/tests/ssr-safety.mjs",
73
73
  "tooling/tests/ssr-safety.d.mts",
74
- "tooling/tsup/index.ts",
74
+ "tooling/tsup/index.mjs",
75
+ "tooling/tsup/index.d.mts",
75
76
  "tooling/biome/biome.json",
76
77
  "tooling/changesets/config.json",
77
78
  "tooling/oxlint/oxlintrc.json",
@@ -147,7 +148,10 @@
147
148
  "types": "./tooling/vitest/jsdom-shims.d.mts",
148
149
  "import": "./tooling/vitest/jsdom-shims.mjs"
149
150
  },
150
- "./tsup": "./tooling/tsup/index.ts",
151
+ "./tsup": {
152
+ "types": "./tooling/tsup/index.d.mts",
153
+ "import": "./tooling/tsup/index.mjs"
154
+ },
151
155
  "./biome": "./tooling/biome/biome.json",
152
156
  "./changesets": "./tooling/changesets/config.json",
153
157
  "./oxlint": "./tooling/oxlint/oxlintrc.json",
@@ -171,7 +175,8 @@
171
175
  "./tests/ssr-safety": {
172
176
  "types": "./tooling/tests/ssr-safety.d.mts",
173
177
  "import": "./tooling/tests/ssr-safety.mjs"
174
- }
178
+ },
179
+ "./package.json": "./package.json"
175
180
  },
176
181
  "dependencies": {
177
182
  "chalk": "^5.6.2",
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "https://biomejs.dev/schemas/2.3.0/schema.json",
2
+ "$schema": "https://biomejs.dev/schemas/2.5.0/schema.json",
3
3
  "vcs": {
4
4
  "enabled": false,
5
5
  "clientKind": "git",
@@ -27,7 +27,7 @@
27
27
  "linter": {
28
28
  "enabled": true,
29
29
  "rules": {
30
- "recommended": true,
30
+ "preset": "recommended",
31
31
  "style": {
32
32
  "noInferrableTypes": "off"
33
33
  },
@@ -51,8 +51,11 @@ export default {
51
51
  changelogFile: 'CHANGELOG.md',
52
52
  },
53
53
  ],
54
- ['@semantic-release/npm', { npmPublish: true, pkgRoot: '.' }],
55
- // NPM plugin to publish the package
54
+ // npm publishing is opt-in via NPM_TOKEN: a repo that provides the token
55
+ // (e.g. js-tooling's own CI) publishes; one that doesn't (GitHub-releases
56
+ // only) gets a green release instead of an EINVALIDNPMTOKEN failure. The
57
+ // version in package.json is still bumped either way.
58
+ ['@semantic-release/npm', { npmPublish: Boolean(process.env.NPM_TOKEN), pkgRoot: '.' }],
56
59
  [
57
60
  '@semantic-release/git',
58
61
  {
@@ -52,8 +52,11 @@ export default {
52
52
  changelogFile: 'CHANGELOG.md',
53
53
  },
54
54
  ],
55
- ['@semantic-release/npm', { npmPublish: true, pkgRoot: '.' }],
56
- // NPM plugin to publish the package
55
+ // npm publishing is opt-in via NPM_TOKEN: a repo that provides the token
56
+ // publishes; one that doesn't (GitLab releases only) gets a green release
57
+ // instead of an EINVALIDNPMTOKEN failure. The version in package.json is
58
+ // still bumped either way.
59
+ ['@semantic-release/npm', { npmPublish: Boolean(process.env.NPM_TOKEN), pkgRoot: '.' }],
57
60
  [
58
61
  '@semantic-release/git',
59
62
  {
@@ -0,0 +1,8 @@
1
+ import type { Options } from 'tsup'
2
+ import type { defineConfig } from 'tsup'
3
+
4
+ export type DefineConfig = ReturnType<typeof defineConfig>
5
+
6
+ export declare const getConfig: (customOptions: Options, env: string) => DefineConfig
7
+
8
+ export declare const baseOptions: (options: Options, env: string) => Options
@@ -0,0 +1,23 @@
1
+ import { defineConfig } from 'tsup'
2
+
3
+ export const getConfig = (customOptions, env) => {
4
+ return defineConfig((options = customOptions) => {
5
+ return baseOptions(options, env)
6
+ })
7
+ }
8
+
9
+ export const baseOptions = (options, env) => {
10
+ const opts = {
11
+ treeshake: true,
12
+ splitting: true,
13
+ format: ['cjs', 'esm'], // generate cjs and esm files
14
+ entry: ['src/**/*.ts'],
15
+ skipNodeModulesBundle: true, // Skips building dependencies for node modules
16
+ minify: !options.watch && env === 'production',
17
+ bundle: false,
18
+ clean: true, // clean up the dist folder
19
+ dts: true, // generate dts file for main module
20
+ ...options,
21
+ }
22
+ return opts
23
+ }
@@ -27,6 +27,10 @@
27
27
  // 📈 Performance
28
28
  "skipLibCheck": true, // Skip type checking of declaration files
29
29
  "incremental": true, // Enable incremental compilation
30
+ // Pin the build-info location so downstream emit tools (e.g. tsup's `dts`
31
+ // build) don't hit TS5074 from inheriting `incremental` without a file.
32
+ // ${configDir} resolves to the consuming project's dir (TS 5.5+).
33
+ "tsBuildInfoFile": "${configDir}/node_modules/.cache/tsconfig.tsbuildinfo",
30
34
  "disableSourceOfProjectReferenceRedirect": true, // Disable source of project reference redirect
31
35
 
32
36
  // 🚨 Strict Type Checking
@@ -1,50 +0,0 @@
1
- import type { Options } from 'tsup'
2
- import { defineConfig } from 'tsup'
3
-
4
- export type DefineConfig = ReturnType<typeof defineConfig>
5
-
6
- export const getConfig: (customOptions: Options, env: string) => DefineConfig = (
7
- customOptions: Options,
8
- env: string
9
- ): DefineConfig => {
10
- return defineConfig((options: Options = customOptions) => {
11
- return baseOptions(options, env)
12
- })
13
- }
14
-
15
- export const baseOptions = (options: Options, env: string): Options => {
16
- const opts: Options = {
17
- treeshake: true,
18
- splitting: true,
19
- // target: 'es2020',
20
- // target: 'nodeNext',
21
- format: ['cjs', 'esm'], // generate cjs and esm files
22
- entry: [
23
- // './src/index.ts',
24
- 'src/**/*.ts',
25
- // './src/**/*!(index).ts?(x)',
26
- // '!./src/**/*.spec.*',
27
- // '!./src/**/*.stories.*',
28
- ],
29
- skipNodeModulesBundle: true, // Skips building dependencies for node modules
30
- minify: !options.watch && env === 'production',
31
- bundle: false, //env === 'production',
32
- clean: true, // clean up the dist folder
33
- dts: true, // generate dts file for main module
34
- // sourcemap: env === 'production', // source map is only available in prod
35
- // sourcemap: true,
36
- // outDir: env === 'production' ? 'dist' : 'lib',
37
- // outDir: 'dist',
38
- // tsconfig: path.resolve(__dirname, './tsconfig.build.json'),
39
- // esbuildOptions(options, context) {
40
- // options.outbase = './'
41
- // },
42
- // external: ['react'],
43
- ...options,
44
- // banner: {js: '"use client";'},
45
- // dts: {
46
- // footer: "declare module 'knex/types/tables';"
47
- // },
48
- }
49
- return opts
50
- }