@rtorcato/repo-tooling 3.8.5 → 3.8.7
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/base/checks.js +26 -7
- package/dist/base/github-settings.js +8 -0
- package/dist/cli/generators/agent-rules.js +20 -2
- package/dist/cli/generators/gitlab-ci.js +7 -2
- package/dist/cli/generators/testing.js +17 -15
- package/dist/cli/utils/lockfile.js +8 -4
- package/dist/languages/js/checks.js +64 -4
- package/dist/languages/js/ci.js +37 -11
- package/dist/languages/js/fixers.js +4 -1
- package/package.json +1 -1
- package/tooling/vitest/vitest.config.d.mts +5 -2
- package/tooling/vitest/vitest.config.mjs +8 -11
- package/tooling/vitest/vitest.config.react.d.mts +2 -2
- package/tooling/vitest/vitest.config.react.mjs +2 -10
package/dist/base/checks.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
|
+
import { BLOCK_START, hasStaleAgentBlock } from '../cli/generators/agent-rules.js';
|
|
3
4
|
import { BADGE_START, hasPublicOnlyBadges } from '../cli/generators/badges.js';
|
|
4
5
|
import { detectNestedLanguages } from '../cli/utils/detect-language.js';
|
|
5
6
|
/**
|
|
@@ -335,19 +336,37 @@ export async function checkCoverageUpload(dir) {
|
|
|
335
336
|
};
|
|
336
337
|
}
|
|
337
338
|
export async function checkAiSetup(dir) {
|
|
338
|
-
// Consider AI setup present if
|
|
339
|
-
// Claude skill is installed — the
|
|
340
|
-
|
|
341
|
-
const
|
|
342
|
-
|
|
339
|
+
// Consider AI setup present if one of the block-managed files carries the
|
|
340
|
+
// agent block or the Claude skill is installed — the markers `fix ai` writes.
|
|
341
|
+
let blockFile = null;
|
|
342
|
+
const stale = [];
|
|
343
|
+
for (const rel of ['AGENTS.md', 'CLAUDE.md', path.join('.github', 'copilot-instructions.md')]) {
|
|
344
|
+
const filepath = path.join(dir, rel);
|
|
345
|
+
if (!(await fs.pathExists(filepath)))
|
|
346
|
+
continue;
|
|
347
|
+
const content = await fs.readFile(filepath, 'utf8');
|
|
348
|
+
if (!content.includes(BLOCK_START))
|
|
349
|
+
continue;
|
|
350
|
+
blockFile ??= rel;
|
|
351
|
+
if (hasStaleAgentBlock(content))
|
|
352
|
+
stale.push(rel);
|
|
353
|
+
}
|
|
343
354
|
const hasSkill = (await fs.pathExists(path.join(dir, '.claude', 'skills', 'repo-tooling.md'))) ||
|
|
344
355
|
// Pre-rename name — still counts as present until `fix` migrates it.
|
|
345
356
|
(await fs.pathExists(path.join(dir, '.claude', 'skills', 'js-tooling.md')));
|
|
346
|
-
if (
|
|
357
|
+
if (stale.length > 0) {
|
|
358
|
+
return {
|
|
359
|
+
check: 'AI setup',
|
|
360
|
+
status: 'drift',
|
|
361
|
+
detail: `${stale.join(', ')} still point agents at the dead \`js-tooling\` package/bin`,
|
|
362
|
+
hint: 'Run `npx @rtorcato/repo-tooling fix ai` to refresh the agent instructions in place',
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
if (blockFile || hasSkill) {
|
|
347
366
|
return {
|
|
348
367
|
check: 'AI setup',
|
|
349
368
|
status: 'ok',
|
|
350
|
-
detail:
|
|
369
|
+
detail: blockFile ? `${blockFile} has the agent block` : '.claude skill installed',
|
|
351
370
|
};
|
|
352
371
|
}
|
|
353
372
|
return {
|
|
@@ -352,6 +352,14 @@ const PROTECTION_BODY = JSON.stringify({
|
|
|
352
352
|
// enforce_admins off so the App/RELEASE_TOKEN can bypass to push release commits.
|
|
353
353
|
enforce_admins: false,
|
|
354
354
|
// Required human review would deadlock solo Dependabot auto-merge.
|
|
355
|
+
//
|
|
356
|
+
// Note this is *applied*, not merely tolerated: checkBranchProtection reports
|
|
357
|
+
// any required_pull_request_reviews as drift, so `fix github-settings` PUTs it
|
|
358
|
+
// back to null. A repo that deliberately requires approvals will have that
|
|
359
|
+
// silently reverted by the next unrelated fix run, with nothing in the output
|
|
360
|
+
// naming the rule that was removed. Known downstream case: the ai-issue-loop
|
|
361
|
+
// pipeline works around it with approval labels precisely because of this.
|
|
362
|
+
// Loosen the standard here first if a repo ever genuinely needs review gating.
|
|
355
363
|
required_pull_request_reviews: null,
|
|
356
364
|
restrictions: null,
|
|
357
365
|
allow_force_pushes: false,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
3
|
import { copyPreset, getPackageRoot } from '../utils/copy-preset.js';
|
|
4
|
+
import { LEGACY_TOOL_NAME } from '../utils/lockfile.js';
|
|
4
5
|
import { installSkillsInstallDocs } from './skills-install.js';
|
|
5
6
|
/**
|
|
6
7
|
* All agent rule files are generated from one source of truth — the shipped
|
|
@@ -8,8 +9,25 @@ import { installSkillsInstallDocs } from './skills-install.js';
|
|
|
8
9
|
* location and the frontmatter differ per agent.
|
|
9
10
|
*/
|
|
10
11
|
const SOURCE = 'tooling/claude/repo-tooling.md';
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
// The markers keep the pre-rename text on purpose: changing them would orphan
|
|
13
|
+
// every block already written and append a second one on the next fix.
|
|
14
|
+
export const BLOCK_START = '<!-- js-tooling:start -->';
|
|
15
|
+
export const BLOCK_END = '<!-- js-tooling:end -->';
|
|
16
|
+
/**
|
|
17
|
+
* True when a managed block still names the pre-rename package or bin — a repo
|
|
18
|
+
* scaffolded before the rename tells agents to run `js-tooling fix ai`, which
|
|
19
|
+
* is not a runnable command (#393). Only the block *body* is inspected: the
|
|
20
|
+
* markers carry the legacy name by design, so matching on the whole file would
|
|
21
|
+
* report every repo as permanently stale.
|
|
22
|
+
*/
|
|
23
|
+
export function hasStaleAgentBlock(content) {
|
|
24
|
+
const start = content.indexOf(BLOCK_START);
|
|
25
|
+
if (start === -1)
|
|
26
|
+
return false;
|
|
27
|
+
const bodyStart = start + BLOCK_START.length;
|
|
28
|
+
const end = content.indexOf(BLOCK_END, bodyStart);
|
|
29
|
+
return content.slice(bodyStart, end === -1 ? undefined : end).includes(LEGACY_TOOL_NAME);
|
|
30
|
+
}
|
|
13
31
|
/** Read the shipped skill and split its frontmatter from the markdown body. */
|
|
14
32
|
async function readSkill() {
|
|
15
33
|
const raw = await fs.readFile(path.join(getPackageRoot(), SOURCE), 'utf8');
|
|
@@ -2,9 +2,14 @@ import path from 'node:path';
|
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
3
|
import { renderGitLabCI } from '../../base/ci.js';
|
|
4
4
|
import { gitlabSpec } from '../../languages/js/ci.js';
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* @param scripts The target's package.json scripts, so the pipeline only calls
|
|
7
|
+
* commands that exist (#386). Omit on the `setup` path, which writes the
|
|
8
|
+
* scripts itself as part of the same scaffold.
|
|
9
|
+
*/
|
|
10
|
+
export async function generateGitLabCI(config, targetDir, { scripts } = {}) {
|
|
6
11
|
const yamlPath = path.join(targetDir, '.gitlab-ci.yml');
|
|
7
12
|
// ponytail: direct JS import until a second language module ships CI (#287).
|
|
8
|
-
await fs.writeFile(yamlPath, renderGitLabCI(gitlabSpec(config)));
|
|
13
|
+
await fs.writeFile(yamlPath, renderGitLabCI(gitlabSpec(config, { scripts })));
|
|
9
14
|
return '.gitlab-ci.yml';
|
|
10
15
|
}
|
|
@@ -22,25 +22,27 @@ export async function generateVitestConfig(config, targetDir) {
|
|
|
22
22
|
// `oxc` (not `esbuild`) is the Vite 8 spelling — vitest ^4 resolves Vite 8,
|
|
23
23
|
// and Vite 8 silently ignores the old `esbuild` key.
|
|
24
24
|
const jsxOverride = config.projectType === 'nextjs-app'
|
|
25
|
-
? `
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
? ` // tsconfig sets jsx: "preserve" for Next's compiler; vitest needs a
|
|
26
|
+
// transform of its own or .tsx tests won't parse.
|
|
27
|
+
oxc: { jsx: { runtime: 'automatic' } },
|
|
28
28
|
`
|
|
29
29
|
: '';
|
|
30
|
-
|
|
30
|
+
// Layered on the shipped preset rather than hand-rolled: the preset owns
|
|
31
|
+
// globals, the shared excludes and the v8/lcov coverage reporters, so preset
|
|
32
|
+
// improvements reach consumers — and doctor's `Vitest` check (which looks for
|
|
33
|
+
// this import) can actually pass after `fix vitest` (#387).
|
|
34
|
+
const vitestConfig = `import base from '@rtorcato/repo-tooling/vitest/config'
|
|
35
|
+
import { defineConfig, mergeConfig } from 'vitest/config'
|
|
31
36
|
|
|
32
|
-
export default
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
provider: 'v8',
|
|
39
|
-
// lcov feeds Codecov; text is the local console summary.
|
|
40
|
-
reporter: ['text', 'lcov']
|
|
37
|
+
export default mergeConfig(
|
|
38
|
+
base,
|
|
39
|
+
defineConfig({
|
|
40
|
+
${jsxOverride} test: {
|
|
41
|
+
environment: '${config.testing.environment === 'browser' ? 'jsdom' : 'node'}',
|
|
42
|
+
setupFiles: ['./vitest.setup.ts']
|
|
41
43
|
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
+
})
|
|
45
|
+
)
|
|
44
46
|
`;
|
|
45
47
|
await fs.writeFile(vitestConfigPath, vitestConfig);
|
|
46
48
|
// Create setup file
|
|
@@ -3,10 +3,14 @@ import fs from 'fs-extra';
|
|
|
3
3
|
import packageJson from '../../../package.json' with { type: 'json' };
|
|
4
4
|
import { validateProjectConfig } from '../commands/setup-presets.js';
|
|
5
5
|
export const LOCKFILE_NAME = '.repo-tooling.json';
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
export const
|
|
6
|
+
// Package and bin name used before the js-tooling→repo-tooling rename (#272).
|
|
7
|
+
// The bin no longer exists and the package is 404 on the registry, so any
|
|
8
|
+
// generated file still naming it is stale (#393).
|
|
9
|
+
export const LEGACY_TOOL_NAME = 'js-tooling';
|
|
10
|
+
// Lockfile name from the same era. Repos set up on an older version still have
|
|
11
|
+
// it: readLockfile falls back to it, and writeLockfile migrates to the new name
|
|
12
|
+
// (removing the old file) on the next write.
|
|
13
|
+
export const LEGACY_LOCKFILE_NAME = `.${LEGACY_TOOL_NAME}.json`;
|
|
10
14
|
// v2 added ProjectConfig.language (multi-language seam, #140). v1 files are
|
|
11
15
|
// migrated to v2 on read, defaulting language to 'js'.
|
|
12
16
|
export const LOCKFILE_VERSION = 2;
|
|
@@ -89,14 +89,74 @@ function matchesBiomeConfig(contents) {
|
|
|
89
89
|
schema.includes('biomejs.dev/schemas/') &&
|
|
90
90
|
linter?.rules?.preset !== undefined);
|
|
91
91
|
}
|
|
92
|
+
const TS_PRESET_REF = /@rtorcato\/(?:js|repo)-tooling\/typescript\//;
|
|
93
|
+
/**
|
|
94
|
+
* Options `tooling/typescript/tsconfig.base.json` sets that a tsconfig written
|
|
95
|
+
* by hand is unlikely to carry all of. `strict` alone is far too common to
|
|
96
|
+
* conclude anything from.
|
|
97
|
+
*/
|
|
98
|
+
const TS_PRESET_STRICTNESS = [
|
|
99
|
+
'strict',
|
|
100
|
+
'noUncheckedIndexedAccess',
|
|
101
|
+
'noImplicitOverride',
|
|
102
|
+
'noPropertyAccessFromIndexSignature',
|
|
103
|
+
];
|
|
104
|
+
/**
|
|
105
|
+
* `strict` and every flag it implies. TypeScript lets any one of these be
|
|
106
|
+
* switched off individually while `strict: true` is still literally present —
|
|
107
|
+
* the explicit override wins — so a config can keep every marker this check
|
|
108
|
+
* looks for and still ship with type safety off. Any of them explicitly
|
|
109
|
+
* `false` is drift.
|
|
110
|
+
*/
|
|
111
|
+
const TS_STRICT_FLAGS = [
|
|
112
|
+
'strict',
|
|
113
|
+
'noImplicitAny',
|
|
114
|
+
'strictNullChecks',
|
|
115
|
+
'strictFunctionTypes',
|
|
116
|
+
'strictBindCallApply',
|
|
117
|
+
'strictPropertyInitialization',
|
|
118
|
+
'noImplicitThis',
|
|
119
|
+
'useUnknownInCatchVariables',
|
|
120
|
+
'alwaysStrict',
|
|
121
|
+
];
|
|
122
|
+
/**
|
|
123
|
+
* The same two shapes as `matchesBiomeConfig`, for the same reason (#385): the
|
|
124
|
+
* `extends` pointer `fix tsconfig` writes, and the whole preset inlined, which
|
|
125
|
+
* is what `copy tsconfig` drops — `tooling/typescript/tsconfig.base.json` *is*
|
|
126
|
+
* the preset, so it names it nowhere, and every `copy tsconfig` repo was
|
|
127
|
+
* reported as drifted with no way out. This check isn't optional, so that read
|
|
128
|
+
* as a real problem rather than an optional one.
|
|
129
|
+
*
|
|
130
|
+
* Structural rather than textual, and for the same reason: `strict: false` is
|
|
131
|
+
* drift whichever shape the config takes, a marker sitting in a comment proves
|
|
132
|
+
* nothing, and unparseable counts as drift rather than being waved through.
|
|
133
|
+
*/
|
|
134
|
+
function matchesTsConfig(contents) {
|
|
135
|
+
const config = parseJsonc(contents);
|
|
136
|
+
if (!config)
|
|
137
|
+
return false;
|
|
138
|
+
const compilerOptions = (config.compilerOptions ?? {});
|
|
139
|
+
// Naming the preset and then switching strictness off is precisely the drift
|
|
140
|
+
// this check exists to catch, so it disqualifies either shape — `strict`
|
|
141
|
+
// itself or any single flag it implies.
|
|
142
|
+
if (TS_STRICT_FLAGS.some((key) => compilerOptions[key] === false))
|
|
143
|
+
return false;
|
|
144
|
+
if (TS_PRESET_REF.test(JSON.stringify(config.extends ?? '')))
|
|
145
|
+
return true;
|
|
146
|
+
// The inlined preset: a `${configDir}`-anchored rootDir — how a preset meant
|
|
147
|
+
// to be extended resolves against the consuming repo — plus the strict block
|
|
148
|
+
// the shipped preset sets.
|
|
149
|
+
const rootDir = compilerOptions.rootDir;
|
|
150
|
+
return (typeof rootDir === 'string' &&
|
|
151
|
+
rootDir.includes('${configDir}') &&
|
|
152
|
+
TS_PRESET_STRICTNESS.every((key) => compilerOptions[key] === true));
|
|
153
|
+
}
|
|
92
154
|
export const FILE_CHECKS = [
|
|
93
155
|
{
|
|
94
156
|
check: 'TypeScript',
|
|
95
157
|
candidates: ['tsconfig.json'],
|
|
96
|
-
expected: `extends "${PACKAGE}/typescript/*"`,
|
|
97
|
-
matcher:
|
|
98
|
-
// `fix tsconfig`, not `copy tsconfig`: copy drops the whole preset inline,
|
|
99
|
-
// which carries no `extends` for the matcher above to find (#381).
|
|
158
|
+
expected: `extends "${PACKAGE}/typescript/*" or inlines the preset, with strict on`,
|
|
159
|
+
matcher: matchesTsConfig,
|
|
100
160
|
hint: 'Run `npx @rtorcato/repo-tooling fix tsconfig` to scaffold',
|
|
101
161
|
},
|
|
102
162
|
{
|
package/dist/languages/js/ci.js
CHANGED
|
@@ -74,9 +74,13 @@ export function scriptsOf(pkg) {
|
|
|
74
74
|
return undefined;
|
|
75
75
|
return pkg.scripts ?? {};
|
|
76
76
|
}
|
|
77
|
+
/** The repo defines this script — or we have no scripts to check it against. */
|
|
78
|
+
function hasScript(opts, script) {
|
|
79
|
+
return !opts.scripts || script in opts.scripts;
|
|
80
|
+
}
|
|
77
81
|
/** Emit a step only when the script it runs exists (or we can't know yet). */
|
|
78
82
|
function stepFor(opts, script, step) {
|
|
79
|
-
return
|
|
83
|
+
return hasScript(opts, script) ? step : null;
|
|
80
84
|
}
|
|
81
85
|
/** Join the steps that survived gating onto the shared setup preamble. */
|
|
82
86
|
function jobSteps(steps) {
|
|
@@ -212,17 +216,33 @@ ${attw}${publint}
|
|
|
212
216
|
}
|
|
213
217
|
return jobs;
|
|
214
218
|
}
|
|
215
|
-
/**
|
|
216
|
-
|
|
219
|
+
/**
|
|
220
|
+
* The `.gitlab-ci.yml` spec for a JS repo — node image, pnpm store cache, pnpm
|
|
221
|
+
* scripts. `opts.scripts` gates each job on the script it runs exactly as
|
|
222
|
+
* `githubJobs` does (#386): `fix gitlab-ci` renders for a repo that already
|
|
223
|
+
* exists and may have none of them, and a job calling a missing script dies
|
|
224
|
+
* with ERR_PNPM_RECURSIVE_EXEC. Undefined still means "assume the setup shape".
|
|
225
|
+
*
|
|
226
|
+
* Dropping the `build` job takes its `artifacts` block with it; no other job
|
|
227
|
+
* references that artifact, so the pipeline stays coherent without it.
|
|
228
|
+
*/
|
|
229
|
+
export function gitlabSpec(config, opts = {}) {
|
|
217
230
|
const hasTypeScript = config.typescript.enabled;
|
|
218
231
|
const hasTests = config.testing.framework !== 'none';
|
|
219
232
|
const hasLint = config.linting.tool !== 'none';
|
|
220
233
|
const hasBuild = config.bundler !== 'none';
|
|
234
|
+
const test = gitlabTest(config);
|
|
221
235
|
const jobs = [
|
|
222
|
-
...(hasLint
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
...(
|
|
236
|
+
...(hasLint && hasScript(opts, lintScript(config))
|
|
237
|
+
? [{ id: 'lint', stage: 'test', script: [lintCommand(config)] }]
|
|
238
|
+
: []),
|
|
239
|
+
...(hasTypeScript && hasScript(opts, 'typecheck')
|
|
240
|
+
? [{ id: 'typecheck', stage: 'test', script: ['pnpm typecheck'] }]
|
|
241
|
+
: []),
|
|
242
|
+
...(hasTests && (test.script === null || hasScript(opts, test.script))
|
|
243
|
+
? [{ id: 'test', stage: 'test', script: [test.command] }]
|
|
244
|
+
: []),
|
|
245
|
+
...(hasBuild && hasScript(opts, 'build')
|
|
226
246
|
? [
|
|
227
247
|
{
|
|
228
248
|
id: 'build',
|
|
@@ -258,10 +278,16 @@ default:
|
|
|
258
278
|
jobs,
|
|
259
279
|
};
|
|
260
280
|
}
|
|
261
|
-
|
|
281
|
+
/**
|
|
282
|
+
* The GitLab test command, paired with the package.json script it needs.
|
|
283
|
+
* Vitest is invoked through `pnpm exec` — a direct binary call, so `script` is
|
|
284
|
+
* null and the job is never gated away; the other frameworks run a script that
|
|
285
|
+
* has to exist.
|
|
286
|
+
*/
|
|
287
|
+
function gitlabTest(config) {
|
|
262
288
|
if (config.testing.framework === 'vitest')
|
|
263
|
-
return 'pnpm exec vitest run';
|
|
289
|
+
return { command: 'pnpm exec vitest run', script: null };
|
|
264
290
|
if (config.testing.framework === 'playwright' || config.testing.framework === 'cypress')
|
|
265
|
-
return 'pnpm test:e2e';
|
|
266
|
-
return 'pnpm test';
|
|
291
|
+
return { command: 'pnpm test:e2e', script: 'test:e2e' };
|
|
292
|
+
return { command: 'pnpm test', script: 'test' };
|
|
267
293
|
}
|
|
@@ -427,7 +427,10 @@ export const FIXERS = [
|
|
|
427
427
|
outputs: ['.gitlab-ci.yml'],
|
|
428
428
|
canFixDrift: true,
|
|
429
429
|
async run({ targetDir, pkg }) {
|
|
430
|
-
|
|
430
|
+
// Only reference scripts this repo actually has (#386).
|
|
431
|
+
const written = await generateGitLabCI(inferProjectConfig(pkg), targetDir, {
|
|
432
|
+
scripts: scriptsOf(pkg),
|
|
433
|
+
});
|
|
431
434
|
return { filesWritten: [written] };
|
|
432
435
|
},
|
|
433
436
|
},
|
package/package.json
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
// vitest 4 re-exports vite's UserConfig under the name ViteUserConfig; there is
|
|
2
|
+
// no `UserConfig` export on 'vitest/config'. Generated configs import this
|
|
3
|
+
// preset now, so a wrong name here surfaces in every consumer's `tsc --noEmit`.
|
|
4
|
+
import type { ViteUserConfig } from 'vitest/config'
|
|
2
5
|
|
|
3
|
-
declare const config:
|
|
6
|
+
declare const config: ViteUserConfig
|
|
4
7
|
export default config
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { dirname, resolve } from 'node:path'
|
|
2
|
-
import { fileURLToPath } from 'node:url'
|
|
3
1
|
import { configDefaults, defineConfig } from 'vitest/config'
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
// No `resolve.alias` here on purpose: this file's `__dirname` is wherever the
|
|
4
|
+
// package got installed, so a `@` → `<preset dir>/src` alias resolves to a
|
|
5
|
+
// directory that exists in no consumer (nor in the published tarball). Now that
|
|
6
|
+
// generated configs actually import this preset, that alias would poison them.
|
|
7
|
+
// Consumers that want `@`/`~` declare them in their own config.
|
|
7
8
|
export default defineConfig({
|
|
8
9
|
test: {
|
|
9
10
|
globals: true,
|
|
@@ -13,13 +14,9 @@ export default defineConfig({
|
|
|
13
14
|
exclude: [...configDefaults.exclude, '.claude/**'],
|
|
14
15
|
coverage: {
|
|
15
16
|
provider: 'v8',
|
|
16
|
-
|
|
17
|
+
// lcov feeds Codecov (the CI we generate uploads it); text is the local
|
|
18
|
+
// console summary; the rest back the HTML report.
|
|
19
|
+
reporter: ['text', 'lcov', 'json', 'html', 'json-summary'],
|
|
17
20
|
},
|
|
18
21
|
},
|
|
19
|
-
resolve: {
|
|
20
|
-
alias: [
|
|
21
|
-
{ find: '@', replacement: resolve(__dirname, './src') },
|
|
22
|
-
{ find: '~', replacement: resolve(__dirname, './src') },
|
|
23
|
-
],
|
|
24
|
-
},
|
|
25
22
|
})
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import base from '@rtorcato/repo-tooling/vitest/config'
|
|
2
2
|
import react from '@vitejs/plugin-react'
|
|
3
|
-
import { dirname, resolve } from 'node:path'
|
|
4
|
-
import { fileURLToPath } from 'node:url'
|
|
5
3
|
import { defineConfig, mergeConfig } from 'vitest/config'
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
// Same reason as the base preset: an alias built from this file's `__dirname`
|
|
6
|
+
// points inside node_modules, never at the consumer's `src`.
|
|
9
7
|
export default mergeConfig(
|
|
10
8
|
base,
|
|
11
9
|
defineConfig({
|
|
@@ -17,11 +15,5 @@ export default mergeConfig(
|
|
|
17
15
|
exclude: ['OLD/**'],
|
|
18
16
|
setupFiles: ['./vitest.setup.ts'],
|
|
19
17
|
},
|
|
20
|
-
resolve: {
|
|
21
|
-
alias: [
|
|
22
|
-
{ find: '@', replacement: resolve(__dirname, './src') },
|
|
23
|
-
{ find: '~', replacement: resolve(__dirname, './src') },
|
|
24
|
-
],
|
|
25
|
-
},
|
|
26
18
|
})
|
|
27
19
|
)
|