@williamthorsen/nmr 0.22.0 → 0.24.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.
@@ -1,36 +1,34 @@
1
1
  import path from 'node:path';
2
2
  import { defaultExclude, mergeConfig } from 'vitest/config';
3
+ import { isObject } from "./helpers/type-guards.js";
3
4
  import { getWorkspacePackageDirs } from "./workspace.js";
4
- const APP_PATTERNS = ['**/__tests__/**/*.app.test.{ts,tsx}'];
5
- const INTEGRATION_PATTERNS = ['**/__tests__/**/*.int.test.{ts,tsx}'];
5
+ const TIER_NAMES = ['unit', 'tool', 'localhost', 'remote'];
6
+ const [RESIDUAL_TIER, ...NAMED_TIERS] = TIER_NAMES;
7
+ const TIERED_PATTERNS = NAMED_TIERS.flatMap(buildTierPatterns);
8
+ const TIER_TIMEOUT = 30_000;
6
9
  const ALL_TEST_PATTERNS = ['**/__tests__/**/*.test.{ts,tsx}'];
7
- const COVERAGE_EXCLUDE = [
8
- '**/__{mocks,tests}__/*',
9
- '**/index.ts',
10
- '**/mock*.{ts,tsx}',
11
- '**/*.d.ts',
12
- '**/*.types.ts',
13
- ];
10
+ const COVERAGE_EXCLUDE = ['**/__{fixtures,mocks,tests}__/**', '**/index.ts', '**/*.d.ts'];
11
+ const BUILD_OUTPUT_EXCLUDE = ['**/dist/**'];
14
12
  const PACKAGE_COVERAGE_INCLUDE = ['**/src/**/*.{ts,tsx}'];
15
- const MISSING_MONOREPO_ROOT = 'defineRootVitestConfig requires `monorepoRoot`, an absolute path to the directory holding pnpm-workspace.yaml. Pass `import.meta.dirname` from the root config.';
16
- export function defineVitestConfig(options = {}) {
17
- return buildConfig(options, { coverageInclude: PACKAGE_COVERAGE_INCLUDE });
13
+ const MISSING_MONOREPO_ROOT = 'defineRootVitestConfig requires `monorepoRoot`, an absolute path to the directory holding pnpm-workspace.yaml. Pass `import.meta.dirname` in the last options layer, from the root config.';
14
+ export function defineVitestConfig(...layers) {
15
+ return buildConfig(layers, { coverageInclude: PACKAGE_COVERAGE_INCLUDE });
18
16
  }
19
- export function defineRootVitestConfig(options) {
20
- if (options === undefined) {
21
- throw new TypeError(MISSING_MONOREPO_ROOT);
22
- }
23
- const monorepoRoot = options.monorepoRoot;
17
+ export function defineRootVitestConfig(...layers) {
18
+ const lastLayer = layers.at(-1);
19
+ const monorepoRoot = isObject(lastLayer) ? lastLayer.monorepoRoot : undefined;
24
20
  if (typeof monorepoRoot !== 'string' || !path.isAbsolute(monorepoRoot)) {
25
21
  throw new TypeError(MISSING_MONOREPO_ROOT);
26
22
  }
27
- return buildConfig(options, {
23
+ return buildConfig(layers, {
28
24
  coverageInclude: [],
29
25
  projectExclude: getWorkspaceExcludePatterns(monorepoRoot),
30
26
  projectRoot: monorepoRoot,
31
27
  });
32
28
  }
33
- function buildConfig(options, { coverageInclude, projectExclude = [], projectRoot }) {
29
+ function buildConfig(declaredLayers, { coverageInclude, projectExclude = [], projectRoot }) {
30
+ const layers = declaredLayers.filter((layer) => layer !== undefined);
31
+ assertKnownTiers(layers);
34
32
  const config = {
35
33
  test: {
36
34
  coverage: {
@@ -40,28 +38,55 @@ function buildConfig(options, { coverageInclude, projectExclude = [], projectRoo
40
38
  provider: 'v8',
41
39
  },
42
40
  passWithNoTests: true,
43
- projects: buildProjects(options.project, projectExclude, projectRoot),
41
+ projects: buildProjects(layers, projectExclude, projectRoot),
44
42
  silent: 'passed-only',
45
43
  watch: false,
46
44
  },
47
45
  };
48
- return options.root ? mergeConfig(config, options.root) : config;
46
+ return layers.reduce((merged, { root }) => (root ? mergeConfig(merged, root) : merged), config);
49
47
  }
50
- function buildProjects(overrides, extraExclude, projectRoot) {
51
- const categories = [
52
- { exclude: [], include: APP_PATTERNS, name: 'app' },
53
- { exclude: [], include: INTEGRATION_PATTERNS, name: 'integration' },
54
- { exclude: [...APP_PATTERNS, ...INTEGRATION_PATTERNS], include: ALL_TEST_PATTERNS, name: 'unit' },
48
+ function buildProjects(layers, extraExclude, projectRoot) {
49
+ const projectTiers = [
50
+ { exclude: TIERED_PATTERNS, include: ALL_TEST_PATTERNS, name: RESIDUAL_TIER },
51
+ ...NAMED_TIERS.map((tier) => ({
52
+ exclude: [],
53
+ include: buildTierPatterns(tier),
54
+ name: tier,
55
+ timeout: TIER_TIMEOUT,
56
+ })),
55
57
  ];
56
- return categories.map(({ exclude, include, name }) => {
58
+ return projectTiers.map(({ exclude, include, name, timeout }) => {
57
59
  const project = {
58
60
  extends: true,
59
61
  ...(projectRoot !== undefined && { root: projectRoot }),
60
- test: { exclude: [...defaultExclude, ...exclude, ...extraExclude], include, name },
62
+ test: {
63
+ exclude: [...defaultExclude, ...BUILD_OUTPUT_EXCLUDE, ...exclude, ...extraExclude],
64
+ include,
65
+ name,
66
+ ...(timeout !== undefined && { hookTimeout: timeout, testTimeout: timeout }),
67
+ },
61
68
  };
62
- return overrides ? mergeConfig(project, { test: overrides }) : project;
69
+ return layers.reduce((merged, layer) => applyLayer(merged, layer, name), project);
63
70
  });
64
71
  }
72
+ function applyLayer(project, layer, tier) {
73
+ const withProject = layer.project ? mergeConfig(project, { test: layer.project }) : project;
74
+ const targeted = layer.tiers?.[tier];
75
+ return targeted ? mergeConfig(withProject, { test: targeted }) : withProject;
76
+ }
77
+ function assertKnownTiers(layers) {
78
+ const known = TIER_NAMES;
79
+ for (const { tiers } of layers) {
80
+ for (const name of Object.keys(tiers ?? {})) {
81
+ if (!known.includes(name)) {
82
+ throw new TypeError(`Unknown tier "${name}" in \`tiers\`. Valid tiers: ${TIER_NAMES.join(', ')}.`);
83
+ }
84
+ }
85
+ }
86
+ }
87
+ function buildTierPatterns(tier) {
88
+ return [`**/__tests__/**/*.${tier}.test.{ts,tsx}`];
89
+ }
65
90
  function getWorkspaceExcludePatterns(monorepoRoot) {
66
91
  return getWorkspacePackageDirs(monorepoRoot)
67
92
  .map((dir) => `${path.relative(monorepoRoot, dir).split(path.sep).join('/')}/**`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@williamthorsen/nmr",
3
- "version": "0.22.0",
3
+ "version": "0.24.0",
4
4
  "private": false,
5
5
  "description": "Context-aware script runner for pnpm monorepos",
6
6
  "keywords": [
@@ -22,9 +22,9 @@
22
22
  "author": "William Thorsen <william@thorsen.dev> (https://github.com/williamthorsen)",
23
23
  "type": "module",
24
24
  "exports": {
25
- ".": {
26
- "types": "./dist/esm/index.d.ts",
27
- "default": "./dist/esm/index.js"
25
+ "./config": {
26
+ "types": "./dist/esm/defineConfig.d.ts",
27
+ "default": "./dist/esm/defineConfig.js"
28
28
  },
29
29
  "./prettier": {
30
30
  "types": "./dist/esm/prettier.d.ts",
@@ -64,11 +64,10 @@
64
64
  ],
65
65
  "dependencies": {
66
66
  "glob": "13.0.6",
67
- "jiti": "2.7.0",
68
67
  "prettier-plugin-sh": "0.19.0",
69
68
  "taze": "19.16.0",
70
69
  "yaml": "2.9.0",
71
- "@williamthorsen/nmr-core": "0.7.3"
70
+ "@williamthorsen/nmr-core": "0.8.0"
72
71
  },
73
72
  "devDependencies": {
74
73
  "prettier": "3.9.6",
@@ -1,2 +0,0 @@
1
- export type { NmrConfig } from './config.js';
2
- export { defineConfig } from './config.js';
package/dist/esm/index.js DELETED
@@ -1 +0,0 @@
1
- export { defineConfig } from "./config.js";