@meza/adr-tools 1.0.11 → 2.0.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.
Files changed (111) hide show
  1. package/.github/renovate.json +2 -1
  2. package/.github/workflows/ci-pr.yml +6 -23
  3. package/.github/workflows/ci.yml +18 -43
  4. package/.releaserc.json +11 -13
  5. package/AGENTS.engineer.md +236 -0
  6. package/AGENTS.md +11 -0
  7. package/AGENTS.reviewer.md +115 -0
  8. package/CONTRIBUTING.md +102 -0
  9. package/README.md +16 -4
  10. package/biome.json +18 -139
  11. package/dist/index.js +164 -81
  12. package/dist/index.js.map +1 -1
  13. package/dist/index.test.js +189 -0
  14. package/dist/index.test.js.map +1 -0
  15. package/dist/inject-version.test.js +27 -0
  16. package/dist/inject-version.test.js.map +1 -0
  17. package/dist/lib/adr.js +132 -27
  18. package/dist/lib/adr.js.map +1 -1
  19. package/dist/lib/adr.test.js +308 -0
  20. package/dist/lib/adr.test.js.map +1 -0
  21. package/dist/lib/config.js +3 -2
  22. package/dist/lib/config.js.map +1 -1
  23. package/dist/lib/config.test.js +60 -0
  24. package/dist/lib/config.test.js.map +1 -0
  25. package/dist/lib/links.test.js +5 -4
  26. package/dist/lib/links.test.js.map +1 -1
  27. package/dist/lib/manipulator-errors.test.js +21 -0
  28. package/dist/lib/manipulator-errors.test.js.map +1 -0
  29. package/dist/lib/manipulator.test.js +21 -1
  30. package/dist/lib/manipulator.test.js.map +1 -1
  31. package/dist/lib/numbering.js +1 -1
  32. package/dist/lib/numbering.js.map +1 -1
  33. package/dist/lib/numbering.test.js +7 -0
  34. package/dist/lib/numbering.test.js.map +1 -1
  35. package/dist/lib/opening.test.js +81 -0
  36. package/dist/lib/opening.test.js.map +1 -0
  37. package/dist/lib/prompt.js +1 -1
  38. package/dist/lib/prompt.js.map +1 -1
  39. package/dist/lib/template.test.js +62 -0
  40. package/dist/lib/template.test.js.map +1 -0
  41. package/dist/types/index.d.ts +25 -0
  42. package/dist/types/index.d.ts.map +1 -1
  43. package/dist/types/index.test.d.ts +2 -0
  44. package/dist/types/index.test.d.ts.map +1 -0
  45. package/dist/types/inject-version.test.d.ts +2 -0
  46. package/dist/types/inject-version.test.d.ts.map +1 -0
  47. package/dist/types/lib/adr.d.ts +15 -0
  48. package/dist/types/lib/adr.d.ts.map +1 -1
  49. package/dist/types/lib/adr.test.d.ts +2 -0
  50. package/dist/types/lib/adr.test.d.ts.map +1 -0
  51. package/dist/types/lib/config.d.ts.map +1 -1
  52. package/dist/types/lib/config.test.d.ts +2 -0
  53. package/dist/types/lib/config.test.d.ts.map +1 -0
  54. package/dist/types/lib/manipulator-errors.test.d.ts +2 -0
  55. package/dist/types/lib/manipulator-errors.test.d.ts.map +1 -0
  56. package/dist/types/lib/opening.test.d.ts +2 -0
  57. package/dist/types/lib/opening.test.d.ts.map +1 -0
  58. package/dist/types/lib/prompt.d.ts.map +1 -1
  59. package/dist/types/lib/template.test.d.ts +2 -0
  60. package/dist/types/lib/template.test.d.ts.map +1 -0
  61. package/dist/types/version.d.ts +1 -1
  62. package/dist/types/version.d.ts.map +1 -1
  63. package/dist/version.js +1 -1
  64. package/dist/version.js.map +1 -1
  65. package/doc/adr/.adr-sequence.lock +1 -1
  66. package/doc/adr/0001-record-architecture-decisions.md +21 -0
  67. package/doc/adr/0002-using-heavy-e2e-tests.md +20 -0
  68. package/doc/adr/0003-esm.md +34 -0
  69. package/doc/adr/0004-gate-editor-opening-behind---open-and---open-with.md +55 -0
  70. package/doc/adr/decisions.md +4 -1
  71. package/lefthook.yml +14 -0
  72. package/package.json +24 -26
  73. package/scripts/inject-version.mjs +34 -0
  74. package/src/index.test.ts +212 -0
  75. package/src/index.ts +229 -108
  76. package/src/inject-version.test.ts +31 -0
  77. package/src/lib/adr.test.ts +376 -0
  78. package/src/lib/adr.ts +173 -27
  79. package/src/lib/config.test.ts +69 -0
  80. package/src/lib/config.ts +3 -2
  81. package/src/lib/links.test.ts +8 -4
  82. package/src/lib/manipulator-errors.test.ts +22 -0
  83. package/src/lib/manipulator.test.ts +25 -1
  84. package/src/lib/numbering.test.ts +8 -0
  85. package/src/lib/numbering.ts +1 -1
  86. package/src/lib/opening.test.ts +96 -0
  87. package/src/lib/prompt.ts +1 -1
  88. package/src/lib/template.test.ts +74 -0
  89. package/src/version.ts +1 -2
  90. package/tests/edit-on-create.e2e.test.ts +47 -16
  91. package/tests/fake-editor.cmd +2 -0
  92. package/tests/fake-visual.cmd +2 -0
  93. package/tests/funny-characters.e2e.test.ts +12 -11
  94. package/tests/generate-graph.e2e.test.ts +13 -17
  95. package/tests/helpers/adr-cli.ts +24 -0
  96. package/tests/init-adr-repository.e2e.test.ts +7 -6
  97. package/tests/linking-records.e2e.test.ts +14 -13
  98. package/tests/list-adrs.e2e.test.ts +26 -19
  99. package/tests/new-adr.e2e.test.ts +10 -9
  100. package/tests/open-with.e2e.test.ts +53 -0
  101. package/tests/superseding-records.e2e.test.ts +11 -10
  102. package/tests/toc-prefixing.e2e.test.ts +10 -9
  103. package/tests/use-template-override.e2e.test.ts +9 -8
  104. package/tests/work-form-other-directories.e2e.test.ts +10 -8
  105. package/vitest.config.e2e.ts +8 -1
  106. package/vitest.config.ts +20 -4
  107. package/.github/workflows/sync-deps-to-main.yml +0 -25
  108. package/.github/workflows/sync-to-deps.yml +0 -26
  109. package/.husky/commit-msg +0 -4
  110. package/CHANGELOG.md +0 -114
  111. package/scripts/inject-version.sh +0 -4
@@ -0,0 +1,24 @@
1
+ import childProcess, { type SpawnSyncOptionsWithStringEncoding } from 'node:child_process';
2
+ import path from 'node:path';
3
+
4
+ export interface AdrCli {
5
+ run(args: string[], options?: { cwd?: string; env?: Record<string, string | undefined>; timeoutMs?: number }): string;
6
+ }
7
+
8
+ export const createAdrCli = (entrypoint: string): AdrCli => {
9
+ const absoluteEntrypoint = path.resolve(entrypoint);
10
+ const tsxCli = path.resolve(process.cwd(), 'node_modules', 'tsx', 'dist', 'cli.mjs');
11
+
12
+ const run: AdrCli['run'] = (args, options) => {
13
+ const result = childProcess.execFileSync(process.execPath, [tsxCli, absoluteEntrypoint, ...args], {
14
+ cwd: options?.cwd,
15
+ env: { ...process.env, ...options?.env },
16
+ timeout: options?.timeoutMs ?? 20000,
17
+ encoding: 'utf8'
18
+ } satisfies SpawnSyncOptionsWithStringEncoding);
19
+
20
+ return result.trimEnd();
21
+ };
22
+
23
+ return { run };
24
+ };
@@ -1,13 +1,13 @@
1
- import * as childProcess from 'child_process';
2
- import * as fs from 'fs';
1
+ import * as fs from 'node:fs';
3
2
  import * as os from 'os';
4
3
  import * as path from 'path';
5
4
  /* eslint-disable no-sync */
6
5
  import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
+ import { createAdrCli } from './helpers/adr-cli';
7
7
 
8
8
  describe('Init an ADR Repository', () => {
9
9
  const adr = path.resolve(path.dirname(__filename), '../src/index.ts');
10
- const command = `npx tsx ${adr}`;
10
+ const cli = createAdrCli(adr);
11
11
 
12
12
  let adrDirectory: string;
13
13
  let workDir: string;
@@ -20,15 +20,16 @@ describe('Init an ADR Repository', () => {
20
20
  });
21
21
 
22
22
  afterEach(() => {
23
- fs.rmdirSync(workDir, {
23
+ fs.rmSync(workDir, {
24
24
  recursive: true,
25
+ force: true,
25
26
  maxRetries: 3,
26
27
  retryDelay: 500
27
28
  });
28
29
  });
29
30
 
30
31
  it('should use the default directory', () => {
31
- childProcess.execSync(`${command} init`, { timeout: 10000, cwd: workDir });
32
+ cli.run(['init'], { cwd: workDir });
32
33
  const expectedFile: string = path.join(adrDirectory, '0001-record-architecture-decisions.md');
33
34
  const expectedLockFile: string = path.join(adrDirectory, '.adr-sequence.lock');
34
35
  expect(fs.existsSync(expectedFile)).toBeTruthy();
@@ -42,7 +43,7 @@ describe('Init an ADR Repository', () => {
42
43
 
43
44
  it('should use an alternate directory', () => {
44
45
  const directory: string = path.resolve(path.join(workDir, 'tmp', 'alternative-dir'));
45
- childProcess.execSync(`${command} init ${directory}`, { timeout: 10000, cwd: workDir });
46
+ cli.run(['init', directory], { cwd: workDir });
46
47
 
47
48
  const expectedInitFile: string = path.join(directory, '0001-record-architecture-decisions.md');
48
49
  const expectedLockFile: string = path.join(directory, '.adr-sequence.lock');
@@ -1,14 +1,14 @@
1
- import * as childProcess from 'child_process';
2
- import { realpathSync, rmdirSync } from 'node:fs';
1
+ import { realpathSync, rmSync } from 'node:fs';
2
+ import * as fs from 'fs/promises';
3
3
  import * as os from 'os';
4
4
  import * as path from 'path';
5
- import * as fs from 'fs/promises';
6
5
  /* eslint-disable no-sync */
7
6
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
7
+ import { createAdrCli } from './helpers/adr-cli';
8
8
 
9
9
  describe('Linking Adrs', () => {
10
10
  const adr = path.resolve(path.dirname(__filename), '../src/index.ts');
11
- const command = `npx tsx ${adr}`;
11
+ const cli = createAdrCli(adr);
12
12
 
13
13
  let adrDirectory: string;
14
14
  let workDir: string;
@@ -22,17 +22,18 @@ describe('Linking Adrs', () => {
22
22
 
23
23
  afterEach(() => {
24
24
  vi.clearAllMocks();
25
- rmdirSync(workDir, {
25
+ rmSync(workDir, {
26
26
  recursive: true,
27
+ force: true,
27
28
  maxRetries: 3,
28
29
  retryDelay: 500
29
30
  });
30
31
  });
31
32
 
32
33
  it('should link adrs as expected with adr new', async () => {
33
- childProcess.execSync(`${command} new First Record`, { timeout: 10000, cwd: workDir });
34
- childProcess.execSync(`${command} new Second Record`, { timeout: 10000, cwd: workDir });
35
- childProcess.execSync(`${command} new -q -l "1:Amends:Amended by" -l "2:Clarifies:Clarified by" Third Record`, {
34
+ cli.run(['new', 'First', 'Record'], { cwd: workDir });
35
+ cli.run(['new', 'Second', 'Record'], { cwd: workDir });
36
+ cli.run(['new', '-q', '-l', '1:Amends:Amended by', '-l', '2:Clarifies:Clarified by', 'Third', 'Record'], {
36
37
  cwd: workDir
37
38
  });
38
39
 
@@ -50,11 +51,11 @@ describe('Linking Adrs', () => {
50
51
  });
51
52
 
52
53
  it('should link adrs as expected with adr link', async () => {
53
- childProcess.execSync(`${command} new First Record`, { timeout: 10000, cwd: workDir });
54
- childProcess.execSync(`${command} new Second Record`, { timeout: 10000, cwd: workDir });
55
- childProcess.execSync(`${command} new Third Record`, { timeout: 10000, cwd: workDir });
56
- childProcess.execSync(`${command} link 3 Amends 1 "Amended by"`, { timeout: 10000, cwd: workDir });
57
- childProcess.execSync(`${command} link 3 Clarifies 2 "Clarified by"`, { timeout: 10000, cwd: workDir });
54
+ cli.run(['new', 'First', 'Record'], { cwd: workDir });
55
+ cli.run(['new', 'Second', 'Record'], { cwd: workDir });
56
+ cli.run(['new', 'Third', 'Record'], { cwd: workDir });
57
+ cli.run(['link', '3', 'Amends', '1', 'Amended by'], { cwd: workDir });
58
+ cli.run(['link', '3', 'Clarifies', '2', 'Clarified by'], { cwd: workDir });
58
59
 
59
60
  const first: string = path.join(adrDirectory, '0001-first-record.md');
60
61
  const second: string = path.join(adrDirectory, '0002-second-record.md');
@@ -1,14 +1,14 @@
1
- import * as childProcess from 'child_process';
2
- import { realpathSync, rmdirSync } from 'node:fs';
1
+ import { realpathSync, rmSync } from 'node:fs';
2
+ import * as fs from 'fs/promises';
3
3
  import * as os from 'os';
4
4
  import * as path from 'path';
5
- import * as fs from 'fs/promises';
6
5
  /* eslint-disable no-sync */
7
6
  import { afterEach, beforeEach, describe, expect, it } from 'vitest';
7
+ import { createAdrCli } from './helpers/adr-cli';
8
8
 
9
9
  describe('Listing', () => {
10
10
  const adr = path.resolve(path.dirname(__filename), '../src/index.ts');
11
- const command = `npx tsx ${adr}`;
11
+ const cli = createAdrCli(adr);
12
12
 
13
13
  let adrDirectory: string;
14
14
  let workDir: string;
@@ -16,38 +16,45 @@ describe('Listing', () => {
16
16
  beforeEach(async () => {
17
17
  workDir = path.resolve(realpathSync(await fs.mkdtemp(path.join(os.tmpdir(), 'adr-'))));
18
18
  adrDirectory = 'doc/adr';
19
- childProcess.execSync(`${command} init ${adrDirectory}`, { timeout: 10000, cwd: workDir });
19
+ cli.run(['init', adrDirectory], { cwd: workDir });
20
20
  });
21
21
 
22
22
  afterEach(() => {
23
- rmdirSync(workDir, {
23
+ rmSync(workDir, {
24
24
  recursive: true,
25
+ force: true,
25
26
  maxRetries: 3,
26
27
  retryDelay: 500
27
28
  });
28
29
  });
29
30
 
30
31
  it('should list an empty directory', async () => {
31
- const child = childProcess.execSync(`${command} list`, { timeout: 10000, cwd: workDir });
32
- const output = child.toString().trim();
33
- expect(output).toEqual('doc/adr/0001-record-architecture-decisions.md');
32
+ const output = cli.run(['list'], { cwd: workDir }).replace(/\r\n/g, '\n');
33
+ expect(output).toEqual(path.join('doc', 'adr', '0001-record-architecture-decisions.md'));
34
34
  });
35
35
 
36
36
  it('should list when there is an additional one', async () => {
37
- childProcess.execSync(`${command} new first`, { timeout: 10000, cwd: workDir });
38
- const child = childProcess.execSync(`${command} list`, { timeout: 10000, cwd: workDir });
39
- const output = child.toString().trim();
40
- expect(output).toEqual('doc/adr/0001-record-architecture-decisions.md\ndoc/adr/0002-first.md');
37
+ cli.run(['new', 'first'], { cwd: workDir });
38
+ const output = cli.run(['list'], { cwd: workDir }).replace(/\r\n/g, '\n');
39
+ expect(output).toEqual(
40
+ [path.join('doc', 'adr', '0001-record-architecture-decisions.md'), path.join('doc', 'adr', '0002-first.md')].join(
41
+ '\n'
42
+ )
43
+ );
41
44
  });
42
45
 
43
46
  it('should list when there are more', async () => {
44
- childProcess.execSync(`${command} new first`, { timeout: 10000, cwd: workDir });
45
- childProcess.execSync(`${command} new second`, { timeout: 10000, cwd: workDir });
46
- childProcess.execSync(`${command} new third`, { timeout: 10000, cwd: workDir });
47
- const child = childProcess.execSync(`${command} list`, { timeout: 10000, cwd: workDir });
48
- const output = child.toString().trim();
47
+ cli.run(['new', 'first'], { cwd: workDir });
48
+ cli.run(['new', 'second'], { cwd: workDir });
49
+ cli.run(['new', 'third'], { cwd: workDir });
50
+ const output = cli.run(['list'], { cwd: workDir }).replace(/\r\n/g, '\n');
49
51
  expect(output).toEqual(
50
- 'doc/adr/0001-record-architecture-decisions.md\ndoc/adr/0002-first.md\ndoc/adr/0003-second.md\ndoc/adr/0004-third.md'
52
+ [
53
+ path.join('doc', 'adr', '0001-record-architecture-decisions.md'),
54
+ path.join('doc', 'adr', '0002-first.md'),
55
+ path.join('doc', 'adr', '0003-second.md'),
56
+ path.join('doc', 'adr', '0004-third.md')
57
+ ].join('\n')
51
58
  );
52
59
  });
53
60
  });
@@ -1,13 +1,13 @@
1
- import * as childProcess from 'child_process';
2
- import * as fs from 'fs';
1
+ import * as fs from 'node:fs';
3
2
  import * as os from 'os';
4
3
  import * as path from 'path';
5
4
  /* eslint-disable no-sync */
6
5
  import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
+ import { createAdrCli } from './helpers/adr-cli';
7
7
 
8
8
  describe('New Adrs', () => {
9
9
  const adr = path.resolve(path.dirname(__filename), '../src/index.ts');
10
- const command = `npx tsx ${adr}`;
10
+ const cli = createAdrCli(adr);
11
11
 
12
12
  let adrDirectory: string;
13
13
  let workDir: string;
@@ -20,16 +20,17 @@ describe('New Adrs', () => {
20
20
  });
21
21
 
22
22
  afterEach(() => {
23
- fs.rmdirSync(workDir, {
23
+ fs.rmSync(workDir, {
24
24
  recursive: true,
25
+ force: true,
25
26
  maxRetries: 3,
26
27
  retryDelay: 500
27
28
  });
28
29
  });
29
30
 
30
31
  it('should create a new one normally', () => {
31
- childProcess.execSync(`${command} init ${adrDirectory}`, { timeout: 10000, cwd: workDir });
32
- childProcess.execSync(`${command} new Example ADR`, { timeout: 10000, cwd: workDir });
32
+ cli.run(['init', adrDirectory], { cwd: workDir });
33
+ cli.run(['new', 'Example', 'ADR'], { cwd: workDir });
33
34
 
34
35
  const expectedNewFile: string = path.join(adrDirectory, '0002-example-adr.md');
35
36
  expect(fs.existsSync(expectedNewFile)).toBeTruthy();
@@ -39,7 +40,7 @@ describe('New Adrs', () => {
39
40
  });
40
41
 
41
42
  it('should create a new one even if no config exists', () => {
42
- childProcess.execSync(`${command} new Example ADR`, { timeout: 10000, cwd: workDir });
43
+ cli.run(['new', 'Example', 'ADR'], { cwd: workDir });
43
44
 
44
45
  const expectedNewFile: string = path.join(adrDirectory, '0001-example-adr.md');
45
46
  expect(fs.existsSync(expectedNewFile)).toBeTruthy();
@@ -49,8 +50,8 @@ describe('New Adrs', () => {
49
50
  });
50
51
 
51
52
  it('should create a table of contents upon creation', () => {
52
- childProcess.execSync(`${command} init ${adrDirectory}`, { timeout: 10000, cwd: workDir });
53
- childProcess.execSync(`${command} new Example ADR`, { timeout: 10000, cwd: workDir });
53
+ cli.run(['init', adrDirectory], { cwd: workDir });
54
+ cli.run(['new', 'Example', 'ADR'], { cwd: workDir });
54
55
 
55
56
  const expectedNewFile: string = path.join(adrDirectory, 'decisions.md');
56
57
  expect(fs.existsSync(expectedNewFile)).toBeTruthy();
@@ -0,0 +1,53 @@
1
+ import * as fs from 'node:fs';
2
+ import * as os from 'node:os';
3
+ import * as path from 'node:path';
4
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
5
+ import { createAdrCli } from './helpers/adr-cli';
6
+
7
+ const waitForFile = async (filePath: string, timeoutMs: number) => {
8
+ const startedAt = Date.now();
9
+ while (Date.now() - startedAt < timeoutMs) {
10
+ if (fs.existsSync(filePath)) {
11
+ return;
12
+ }
13
+ await new Promise((r) => setTimeout(r, 25));
14
+ }
15
+ throw new Error(`Timed out waiting for file: ${filePath}`);
16
+ };
17
+
18
+ describe('--open-with', () => {
19
+ const adr = path.resolve(path.dirname(__filename), '../src/index.ts');
20
+ const cli = createAdrCli(adr);
21
+ const visualHelper =
22
+ process.platform === 'win32'
23
+ ? path.resolve(path.dirname(__filename), './fake-visual.cmd')
24
+ : path.resolve(path.dirname(__filename), './fake-visual');
25
+
26
+ let workDir: string;
27
+ let adrDirectory: string;
28
+
29
+ beforeEach(() => {
30
+ // @ts-ignore
31
+ process.env.ADR_DATE = '1992-01-12';
32
+ workDir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'adr-')));
33
+ adrDirectory = path.resolve(path.join(workDir, 'doc/adr'));
34
+ });
35
+
36
+ afterEach(async () => {
37
+ if (process.platform === 'win32') {
38
+ await new Promise((r) => setTimeout(r, 500));
39
+ }
40
+ fs.rmSync(workDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 250 });
41
+ });
42
+
43
+ it('opens the created ADR with the provided command', async () => {
44
+ cli.run(['new', '--open-with', visualHelper, 'Example', 'ADR'], { cwd: workDir });
45
+
46
+ const expectedOpenMarker: string = path.join(workDir, 'visual.out');
47
+ await waitForFile(expectedOpenMarker, 2000);
48
+
49
+ const contents = fs.readFileSync(expectedOpenMarker, 'utf8').trim();
50
+ const openedPath = contents.replace(/^VISUAL\s+/, '');
51
+ expect(path.normalize(openedPath)).toEqual(path.normalize(`${adrDirectory}/0001-example-adr.md`));
52
+ });
53
+ });
@@ -1,14 +1,14 @@
1
- import * as childProcess from 'child_process';
2
- import { realpathSync, rmdirSync } from 'node:fs';
1
+ import { realpathSync, rmSync } from 'node:fs';
2
+ import * as fs from 'fs/promises';
3
3
  import * as os from 'os';
4
4
  import * as path from 'path';
5
- import * as fs from 'fs/promises';
6
5
  /* eslint-disable no-sync */
7
6
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
7
+ import { createAdrCli } from './helpers/adr-cli';
8
8
 
9
9
  describe('Superseding Adrs', () => {
10
10
  const adr = path.resolve(path.dirname(__filename), '../src/index.ts');
11
- const command = `npx tsx ${adr}`;
11
+ const cli = createAdrCli(adr);
12
12
 
13
13
  let adrDirectory: string;
14
14
  let workDir: string;
@@ -22,16 +22,17 @@ describe('Superseding Adrs', () => {
22
22
 
23
23
  afterEach(() => {
24
24
  vi.clearAllMocks();
25
- rmdirSync(workDir, {
25
+ rmSync(workDir, {
26
26
  recursive: true,
27
+ force: true,
27
28
  maxRetries: 3,
28
29
  retryDelay: 500
29
30
  });
30
31
  });
31
32
 
32
33
  it('should be able to supersede previous adrs', async () => {
33
- childProcess.execSync(`${command} new First Record`, { timeout: 10000, cwd: workDir });
34
- childProcess.execSync(`${command} new -s 1 Second Record`, { timeout: 10000, cwd: workDir });
34
+ cli.run(['new', 'First', 'Record'], { cwd: workDir });
35
+ cli.run(['new', '-s', '1', 'Second', 'Record'], { cwd: workDir });
35
36
 
36
37
  const first: string = path.join(adrDirectory, '0001-first-record.md');
37
38
  const second: string = path.join(adrDirectory, '0002-second-record.md');
@@ -44,9 +45,9 @@ describe('Superseding Adrs', () => {
44
45
  });
45
46
 
46
47
  it('should be able to supersede multiple records', async () => {
47
- childProcess.execSync(`${command} new First Record`, { timeout: 10000, cwd: workDir });
48
- childProcess.execSync(`${command} new Second Record`, { timeout: 10000, cwd: workDir });
49
- childProcess.execSync(`${command} new -s 1 -s 2 Third Record`, { timeout: 10000, cwd: workDir });
48
+ cli.run(['new', 'First', 'Record'], { cwd: workDir });
49
+ cli.run(['new', 'Second', 'Record'], { cwd: workDir });
50
+ cli.run(['new', '-s', '1', '-s', '2', 'Third', 'Record'], { cwd: workDir });
50
51
 
51
52
  const first: string = path.join(adrDirectory, '0001-first-record.md');
52
53
  const second: string = path.join(adrDirectory, '0002-second-record.md');
@@ -1,14 +1,14 @@
1
- import * as childProcess from 'child_process';
2
- import { realpathSync, rmdirSync } from 'node:fs';
1
+ import { realpathSync, rmSync } from 'node:fs';
2
+ import * as fs from 'fs/promises';
3
3
  import * as os from 'os';
4
4
  import * as path from 'path';
5
- import * as fs from 'fs/promises';
6
5
  /* eslint-disable no-sync */
7
6
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
7
+ import { createAdrCli } from './helpers/adr-cli';
8
8
 
9
9
  describe('Generating TOC', () => {
10
10
  const adr = path.resolve(path.dirname(__filename), '../src/index.ts');
11
- const command = `npx tsx ${adr}`;
11
+ const cli = createAdrCli(adr);
12
12
 
13
13
  let adrDirectory: string;
14
14
  let workDir: string;
@@ -22,18 +22,19 @@ describe('Generating TOC', () => {
22
22
 
23
23
  afterEach(() => {
24
24
  vi.clearAllMocks();
25
- rmdirSync(workDir, {
25
+ rmSync(workDir, {
26
26
  recursive: true,
27
+ force: true,
27
28
  maxRetries: 3,
28
29
  retryDelay: 500
29
30
  });
30
31
  });
31
32
 
32
33
  it('should add a path prefix to the toc when there is one supplied', async () => {
33
- childProcess.execSync(`${command} new First Record`, { timeout: 10000, cwd: workDir });
34
- childProcess.execSync(`${command} new Second Record`, { timeout: 10000, cwd: workDir });
35
- childProcess.execSync(`${command} new Third Record`, { timeout: 10000, cwd: workDir });
36
- childProcess.execSync(`${command} generate toc -p foo/doc/adr/`, { timeout: 10000, cwd: workDir });
34
+ cli.run(['new', 'First', 'Record'], { cwd: workDir });
35
+ cli.run(['new', 'Second', 'Record'], { cwd: workDir });
36
+ cli.run(['new', 'Third', 'Record'], { cwd: workDir });
37
+ cli.run(['generate', 'toc', '-p', 'foo/doc/adr/'], { cwd: workDir });
37
38
 
38
39
  const tocFilePath: string = path.join(adrDirectory, 'decisions.md');
39
40
  const tocContent = await fs.readFile(tocFilePath, 'utf8');
@@ -1,14 +1,14 @@
1
- import * as childProcess from 'child_process';
2
- import { realpathSync, rmdirSync } from 'node:fs';
1
+ import { realpathSync, rmSync } from 'node:fs';
2
+ import * as fs from 'fs/promises';
3
3
  import * as os from 'os';
4
4
  import * as path from 'path';
5
- import * as fs from 'fs/promises';
6
5
  /* eslint-disable no-sync */
7
6
  import { afterEach, beforeEach, describe, expect, it } from 'vitest';
7
+ import { createAdrCli } from './helpers/adr-cli';
8
8
 
9
9
  describe('Overriding templates', () => {
10
10
  const adr = path.resolve(path.dirname(__filename), '../src/index.ts');
11
- const command = `npx tsx ${adr}`;
11
+ const cli = createAdrCli(adr);
12
12
 
13
13
  let adrDirectory: string;
14
14
  let workDir: string;
@@ -18,12 +18,13 @@ describe('Overriding templates', () => {
18
18
  process.env.ADR_DATE = '1992-01-12';
19
19
  workDir = path.resolve(realpathSync(await fs.mkdtemp(path.join(os.tmpdir(), 'adr-'))));
20
20
  adrDirectory = path.join(workDir, 'doc/adr');
21
- childProcess.execSync(`${command} init ${adrDirectory}`, { timeout: 10000, cwd: workDir });
21
+ cli.run(['init', adrDirectory], { cwd: workDir });
22
22
  });
23
23
 
24
24
  afterEach(() => {
25
- rmdirSync(workDir, {
25
+ rmSync(workDir, {
26
26
  recursive: true,
27
+ force: true,
27
28
  maxRetries: 3,
28
29
  retryDelay: 500
29
30
  });
@@ -36,8 +37,8 @@ describe('Overriding templates', () => {
36
37
  '# This is an override template\nTITLE\nDATE\nNUMBER\nSTATUS'
37
38
  );
38
39
 
39
- childProcess.execSync(`${command} new Example ADR`, { timeout: 10000, cwd: workDir });
40
- childProcess.execSync(`${command} new Another Example ADR`, { timeout: 10000, cwd: workDir });
40
+ cli.run(['new', 'Example', 'ADR'], { cwd: workDir });
41
+ cli.run(['new', 'Another', 'Example', 'ADR'], { cwd: workDir });
41
42
 
42
43
  const expectedFile: string = path.join(adrDirectory, '0002-example-adr.md');
43
44
  const expectedFile2: string = path.join(adrDirectory, '0003-another-example-adr.md');
@@ -1,13 +1,13 @@
1
- import * as childProcess from 'child_process';
2
- import * as fs from 'fs';
1
+ import * as fs from 'node:fs';
3
2
  import * as os from 'os';
4
3
  import * as path from 'path';
5
4
  /* eslint-disable no-sync */
6
5
  import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
+ import { createAdrCli } from './helpers/adr-cli';
7
7
 
8
8
  describe('deep directories', () => {
9
9
  const adr = path.resolve(path.dirname(__filename), '../src/index.ts');
10
- const command = `npx tsx ${adr}`;
10
+ const cli = createAdrCli(adr);
11
11
 
12
12
  let adrDirectory: string;
13
13
  let workDir: string;
@@ -15,12 +15,13 @@ describe('deep directories', () => {
15
15
  beforeEach(() => {
16
16
  workDir = path.resolve(fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'adr-'))));
17
17
  adrDirectory = path.join(workDir, 'doc/adr');
18
- childProcess.execSync(`${command} init ${adrDirectory}`, { timeout: 10000, cwd: workDir });
18
+ cli.run(['init', adrDirectory], { cwd: workDir });
19
19
  });
20
20
 
21
21
  afterEach(() => {
22
- fs.rmdirSync(workDir, {
22
+ fs.rmSync(workDir, {
23
23
  recursive: true,
24
+ force: true,
24
25
  maxRetries: 3,
25
26
  retryDelay: 500
26
27
  });
@@ -29,17 +30,18 @@ describe('deep directories', () => {
29
30
  it('can work', () => {
30
31
  const innerPath = path.join(fs.mkdtempSync(path.resolve(workDir) + '/'), 'inner');
31
32
  fs.mkdirSync(innerPath, { recursive: true });
32
- childProcess.execSync(`${command} new this should exist`, { timeout: 10000, cwd: innerPath });
33
+ cli.run(['new', 'this', 'should', 'exist'], { cwd: innerPath });
33
34
  const expectedFile: string = path.join(adrDirectory, '0002-this-should-exist.md');
34
35
  expect(fs.existsSync(expectedFile)).toBeTruthy();
35
36
  });
36
37
 
37
38
  it('can work when there has been no config initiated', () => {
38
- childProcess.execSync(`rimraf ${adrDirectory} ${workDir}/.adr-dir`);
39
+ fs.rmSync(adrDirectory, { recursive: true, force: true });
40
+ fs.rmSync(path.join(workDir, '.adr-dir'), { force: true });
39
41
 
40
42
  const innerPath = path.join(fs.mkdtempSync(path.resolve(workDir) + '/'), 'inner');
41
43
  fs.mkdirSync(innerPath, { recursive: true });
42
- childProcess.execSync(`${command} new this should exist`, { timeout: 10000, cwd: innerPath });
44
+ cli.run(['new', 'this', 'should', 'exist'], { cwd: innerPath });
43
45
  const expectedFile: string = path.join(innerPath, 'doc', 'adr', '0001-this-should-exist.md');
44
46
  expect(fs.existsSync(expectedFile)).toBeTruthy();
45
47
  });
@@ -3,7 +3,14 @@ import { defineConfig } from 'vitest/config';
3
3
  export default defineConfig({
4
4
  test: {
5
5
  dir: 'tests',
6
- testTimeout: 10000,
6
+ testTimeout: 30000,
7
+ hookTimeout: 30000,
8
+ pool: 'threads',
9
+ poolOptions: {
10
+ threads: {
11
+ singleThread: true
12
+ }
13
+ },
7
14
  watch: false,
8
15
  coverage: {
9
16
  reportsDirectory: './reports/coverage/e2e',
package/vitest.config.ts CHANGED
@@ -3,15 +3,31 @@ import { defineConfig } from 'vitest/config';
3
3
  export default defineConfig({
4
4
  test: {
5
5
  dir: 'src',
6
- testTimeout: 10000,
6
+ testTimeout: 30000,
7
+ hookTimeout: 30000,
8
+ pool: 'threads',
7
9
  watch: false,
10
+ fileParallelism: false,
11
+ maxWorkers: 1,
12
+ minWorkers: 1,
8
13
  poolOptions: {
9
- forks: {
10
- singleFork: true,
11
- isolate: true
14
+ threads: {
15
+ isolate: false,
16
+ singleThread: true
12
17
  }
13
18
  },
14
19
  coverage: {
20
+ include: ['src/**/*.ts'],
21
+ exclude: ['**/node_modules/**', '**/*.d.ts'],
22
+ all: false,
23
+ processingConcurrency: 1,
24
+ thresholds: {
25
+ branches: 100,
26
+ functions: 100,
27
+ lines: 100,
28
+ statements: 100,
29
+ perFile: true
30
+ },
15
31
  reportsDirectory: './reports/coverage/unit',
16
32
  reporter: ['text', 'json', 'html']
17
33
  }
@@ -1,25 +0,0 @@
1
- name: Sync Deps to Main
2
- on:
3
- schedule:
4
- - cron: '9 0 * * 4'
5
-
6
- jobs:
7
- sync-branches:
8
- runs-on: ubuntu-latest
9
- name: Syncing dependencies
10
- steps:
11
- - name: Checkout
12
- uses: actions/checkout@v4
13
- - name: Set up Node
14
- uses: actions/setup-node@v4
15
- with:
16
- node-version: 12
17
- - name: Opening pull request
18
- id: pull
19
- uses: tretuna/sync-branches@1.4.0
20
- with:
21
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
22
- FROM_BRANCH: "dependency-update"
23
- TO_BRANCH: "main"
24
- PULL_REQUEST_TITLE: "chore: sync deps to main"
25
- PULL_REQUEST_AUTO_MERGE_METHOD: "merge"
@@ -1,26 +0,0 @@
1
- name: Sync Main to Deps
2
- on:
3
- push:
4
- branches:
5
- - main
6
-
7
- jobs:
8
- sync-branches:
9
- runs-on: ubuntu-latest
10
- name: Syncing branches
11
- steps:
12
- - name: Checkout
13
- uses: actions/checkout@v4
14
- - name: Set up Node
15
- uses: actions/setup-node@v4
16
- with:
17
- node-version: 12
18
- - name: Opening pull request
19
- id: pull
20
- uses: tretuna/sync-branches@1.4.0
21
- with:
22
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
23
- FROM_BRANCH: "main"
24
- TO_BRANCH: "dependency-update"
25
- PULL_REQUEST_TITLE: "chore: sync main to deps"
26
- PULL_REQUEST_AUTO_MERGE_METHOD: "merge"
package/.husky/commit-msg DELETED
@@ -1,4 +0,0 @@
1
- #!/bin/sh
2
- . "$(dirname "$0")/_/husky.sh"
3
-
4
- npx --no -- commitlint --edit "${1}"