@meza/adr-tools 1.0.8 → 1.0.11

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 (109) hide show
  1. package/.gitattributes +40 -0
  2. package/.github/renovate.json +5 -0
  3. package/.github/workflows/ci-pr.yml +44 -0
  4. package/.github/workflows/ci.yml +21 -22
  5. package/.github/workflows/sync-deps-to-main.yml +25 -0
  6. package/.github/workflows/sync-to-deps.yml +26 -0
  7. package/.releaserc.json +2 -7
  8. package/CHANGELOG.md +84 -0
  9. package/LICENSE +674 -0
  10. package/README.md +64 -5
  11. package/biome.json +148 -0
  12. package/dist/index.js +105 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/lib/adr.js +177 -0
  15. package/dist/lib/adr.js.map +1 -0
  16. package/dist/lib/config.js +33 -0
  17. package/dist/lib/config.js.map +1 -0
  18. package/dist/lib/links.js +25 -0
  19. package/dist/lib/links.js.map +1 -0
  20. package/dist/lib/links.test.js +65 -0
  21. package/dist/lib/links.test.js.map +1 -0
  22. package/dist/lib/manipulator.js +84 -0
  23. package/dist/lib/manipulator.js.map +1 -0
  24. package/dist/lib/manipulator.test.js +76 -0
  25. package/dist/lib/manipulator.test.js.map +1 -0
  26. package/dist/lib/numbering.js +25 -0
  27. package/dist/lib/numbering.js.map +1 -0
  28. package/dist/lib/numbering.test.js +32 -0
  29. package/dist/lib/numbering.test.js.map +1 -0
  30. package/dist/lib/prompt.js +14 -0
  31. package/dist/lib/prompt.js.map +1 -0
  32. package/dist/lib/prompt.test.js +33 -0
  33. package/dist/lib/prompt.test.js.map +1 -0
  34. package/dist/lib/template.js +21 -0
  35. package/dist/lib/template.js.map +1 -0
  36. package/{doc/adr/0001-record-architecture-decisions.md → dist/templates/init.md} +2 -2
  37. package/dist/templates/template.md +19 -0
  38. package/dist/types/index.d.ts +3 -0
  39. package/dist/types/index.d.ts.map +1 -0
  40. package/dist/types/lib/adr.d.ts +18 -0
  41. package/dist/types/lib/adr.d.ts.map +1 -0
  42. package/dist/types/lib/config.d.ts +3 -0
  43. package/dist/types/lib/config.d.ts.map +1 -0
  44. package/dist/types/lib/links.d.ts +10 -0
  45. package/dist/types/lib/links.d.ts.map +1 -0
  46. package/dist/types/lib/links.test.d.ts +2 -0
  47. package/dist/types/lib/links.test.d.ts.map +1 -0
  48. package/dist/types/lib/manipulator.d.ts +11 -0
  49. package/dist/types/lib/manipulator.d.ts.map +1 -0
  50. package/dist/types/lib/manipulator.test.d.ts +2 -0
  51. package/dist/types/lib/manipulator.test.d.ts.map +1 -0
  52. package/dist/types/lib/numbering.d.ts +2 -0
  53. package/dist/types/lib/numbering.d.ts.map +1 -0
  54. package/dist/types/lib/numbering.test.d.ts +2 -0
  55. package/dist/types/lib/numbering.test.d.ts.map +1 -0
  56. package/dist/types/lib/prompt.d.ts +2 -0
  57. package/dist/types/lib/prompt.d.ts.map +1 -0
  58. package/dist/types/lib/prompt.test.d.ts +2 -0
  59. package/dist/types/lib/prompt.test.d.ts.map +1 -0
  60. package/dist/types/lib/template.d.ts +2 -0
  61. package/dist/types/lib/template.d.ts.map +1 -0
  62. package/dist/types/version.d.ts +2 -0
  63. package/dist/types/version.d.ts.map +1 -0
  64. package/dist/version.js +2 -0
  65. package/dist/version.js.map +1 -0
  66. package/doc/adr/.adr-sequence.lock +1 -0
  67. package/doc/adr/decisions.md +3 -0
  68. package/package.json +78 -48
  69. package/src/index.ts +52 -27
  70. package/src/lib/adr.ts +67 -72
  71. package/src/lib/config.ts +3 -3
  72. package/src/lib/links.test.ts +8 -24
  73. package/src/lib/links.ts +2 -2
  74. package/src/lib/manipulator.test.ts +44 -47
  75. package/src/lib/manipulator.ts +22 -10
  76. package/src/lib/numbering.test.ts +5 -9
  77. package/src/lib/numbering.ts +4 -5
  78. package/src/lib/prompt.test.ts +42 -0
  79. package/src/lib/prompt.ts +14 -0
  80. package/src/lib/template.ts +7 -3
  81. package/src/version.ts +1 -1
  82. package/tests/.adr-dir +1 -0
  83. package/tests/__snapshots__/generate-graph.e2e.test.ts.snap +23 -23
  84. package/tests/__snapshots__/init-adr-repository.e2e.test.ts.snap +1 -1
  85. package/tests/__snapshots__/linking-records.e2e.test.ts.snap +1 -1
  86. package/tests/__snapshots__/new-adr.e2e.test.ts.snap +1 -1
  87. package/tests/__snapshots__/superseding-records.e2e.test.ts.snap +1 -1
  88. package/tests/__snapshots__/toc-prefixing.e2e.test.ts.snap +1 -1
  89. package/tests/__snapshots__/use-template-override.e2e.test.ts.snap +1 -1
  90. package/tests/edit-on-create.e2e.test.ts +17 -12
  91. package/tests/funny-characters.e2e.test.ts +28 -21
  92. package/tests/generate-graph.e2e.test.ts +21 -13
  93. package/tests/init-adr-repository.e2e.test.ts +12 -8
  94. package/tests/linking-records.e2e.test.ts +21 -14
  95. package/tests/list-adrs.e2e.test.ts +23 -18
  96. package/tests/new-adr.e2e.test.ts +15 -12
  97. package/tests/superseding-records.e2e.test.ts +16 -11
  98. package/tests/toc-prefixing.e2e.test.ts +15 -11
  99. package/tests/use-template-override.e2e.test.ts +18 -10
  100. package/tests/work-form-other-directories.e2e.test.ts +14 -12
  101. package/tsconfig.json +9 -8
  102. package/vitest.config.e2e.ts +13 -0
  103. package/vitest.config.ts +8 -1
  104. package/.eslintignore +0 -2
  105. package/.eslintrc.json +0 -23
  106. package/.github/dependabot.yml +0 -14
  107. package/.github/workflows/auto-merge.yml +0 -14
  108. package/doc/adr/0002-using-heavy-e2e-tests.md +0 -20
  109. /package/src/{environment.d.ts → types/environment.d.ts} +0 -0
package/package.json CHANGED
@@ -1,40 +1,51 @@
1
1
  {
2
2
  "name": "@meza/adr-tools",
3
- "version": "1.0.8",
4
- "main": "dist/index.js",
3
+ "version": "1.0.11",
4
+ "exports": {
5
+ ".": {
6
+ "import": {
7
+ "types": "dist/types/index.d.ts",
8
+ "default": "dist/index.js"
9
+ }
10
+ }
11
+ },
5
12
  "bin": {
6
13
  "adr": "dist/index.js"
7
14
  },
15
+ "license": "GPL-3.0",
16
+ "type": "module",
8
17
  "types": "dist/index.d.ts",
9
18
  "private": false,
10
19
  "scripts": {
11
- "prebuild": "node -p \"'export const LIB_VERSION = ' + require('./package.json').version + ';'\" > src/version.ts",
12
- "build": "tsc",
20
+ "prebuild": "./scripts/inject-version.sh ./src/version.ts",
21
+ "prepack": "./scripts/inject-version.sh ./src/version.ts && tsc && yarn copy ",
22
+ "copy": "copyfiles -u 1 ./src/templates/** ./dist",
23
+ "build": "tsc && yarn copy",
13
24
  "start": "ts-node src/index.ts",
14
- "commit": "commit",
15
- "ci": "npm set editor '' && yarn lint && yarn test",
25
+ "commit": "cz",
26
+ "ci": "npm set editor 'true' && npm-run-all --parallel lint test",
16
27
  "ci:dev": "exit 0",
17
- "clean": "rm -rf dist .cache/tsbuildinfo",
28
+ "clean": "rimraf dist .cache/tsbuildinfo",
18
29
  "clean:all": "yarn clean && rm -rf node_modules .cache",
19
- "lint:eslint": "eslint . --ext .ts --ext .json --cache --cache-location .cache/",
30
+ "lint:ci": "biome ci --reporter=github",
20
31
  "lint:tsc": "tsc --noEmit",
21
- "lint:fix": "yarn lint:eslint --fix && yarn lint:tsc",
22
- "lint": "yarn lint:eslint && yarn lint:tsc",
23
- "test": "vitest",
32
+ "lint": "npm-run-all --parallel lint:*",
33
+ "test": "npm-run-all --parallel test:*",
34
+ "test:unit": "vitest",
35
+ "test:e2e": "vitest --config vitest.config.e2e.ts",
24
36
  "prepare": "is-ci || husky install",
25
37
  "report": "exit 0",
26
38
  "semantic-release": "semantic-release",
27
39
  "release": "semantic-release"
28
40
  },
29
41
  "dependencies": {
30
- "@types/inquirer": "^8.2.1",
31
- "@types/marked": "^4.0.3",
32
- "@types/node": "^18.0.0",
33
- "chalk": "^4.1.2",
34
- "commander": "^9.3.0",
35
- "core-js": "^3.19.1",
36
- "inquirer": "^8.2.4",
37
- "marked": "^4.0.17"
42
+ "@types/inquirer": "9.0.8",
43
+ "@types/node": "22.15.21",
44
+ "chalk": "5.4.1",
45
+ "commander": "12.1.0",
46
+ "core-js": "3.42.0",
47
+ "inquirer": "9.3.7",
48
+ "marked": "4.3.0"
38
49
  },
39
50
  "commitlint": {
40
51
  "extends": [
@@ -50,35 +61,39 @@
50
61
  }
51
62
  },
52
63
  "devDependencies": {
53
- "@commitlint/cli": "^17.0.2",
54
- "@commitlint/config-conventional": "^17.0.2",
55
- "@commitlint/prompt-cli": "^17.0.0",
56
- "@meza/tsconfig-base": "^1.1.0",
57
- "@semantic-release/changelog": "^6.0.1",
58
- "@semantic-release/commit-analyzer": "^9.0.2",
59
- "@semantic-release/git": "^10.0.1",
60
- "@semantic-release/github": "^8.0.4",
61
- "@semantic-release/npm": "^9.0.1",
62
- "@semantic-release/release-notes-generator": "^10.0.3",
63
- "@types/uuid": "^8.3.4",
64
- "@typescript-eslint/eslint-plugin": "^5.29.0",
65
- "@typescript-eslint/parser": "^5.3.0",
66
- "@vitest/ui": "^0.15.2",
67
- "c8": "^7.11.3",
68
- "eslint": "^8.1.0",
69
- "eslint-config-tailored-tunes": "^5.0.2",
70
- "eslint-plugin-json": "^3.1.0",
71
- "eslint-plugin-security": "^1.5.0",
72
- "husky": "^8.0.1",
73
- "install-deps-postmerge": "^2.0.1",
74
- "is-ci": "^3.0.1",
75
- "mock-cwd": "^1.0.0",
76
- "semantic-release": "^19.0.3",
77
- "ts-node": "^10.8.1",
78
- "typescript": "^4.4.4",
79
- "uuid": "^8.3.2",
80
- "vitest": "^0.15.2",
81
- "yarn": "^1.22.17"
64
+ "@biomejs/biome": "1.9.4",
65
+ "@commitlint/cli": "19.8.1",
66
+ "@commitlint/config-conventional": "19.8.1",
67
+ "@commitlint/prompt-cli": "19.8.1",
68
+ "@faker-js/faker": "9.8.0",
69
+ "@meza/adr-tools": "1.0.10",
70
+ "@meza/tsconfig-base": "1.1.0",
71
+ "@ryansonshine/commitizen": "4.2.8",
72
+ "@ryansonshine/cz-conventional-changelog": "3.3.4",
73
+ "@semantic-release/changelog": "6.0.3",
74
+ "@semantic-release/commit-analyzer": "13.0.1",
75
+ "@semantic-release/git": "10.0.1",
76
+ "@semantic-release/github": "11.0.2",
77
+ "@semantic-release/npm": "12.0.1",
78
+ "@semantic-release/release-notes-generator": "14.0.3",
79
+ "@types/uuid": "10.0.0",
80
+ "@vitest/ui": "3.1.4",
81
+ "copyfiles": "2.4.1",
82
+ "cross-env": "7.0.3",
83
+ "husky": "9.1.7",
84
+ "install-deps-postmerge": "2.0.1",
85
+ "is-ci": "4.1.0",
86
+ "lint-staged": "16.0.0",
87
+ "mock-cwd": "1.0.0",
88
+ "npm-run-all2": "8.0.3",
89
+ "rimraf": "6.0.1",
90
+ "semantic-release": "24.2.4",
91
+ "ts-node": "10.9.2",
92
+ "tsx": "4.19.4",
93
+ "typescript": "5.8.3",
94
+ "uuid": "11.1.0",
95
+ "vitest": "3.1.4",
96
+ "yarn": "1.22.22"
82
97
  },
83
98
  "repository": {
84
99
  "type": "git",
@@ -86,5 +101,20 @@
86
101
  },
87
102
  "publishConfig": {
88
103
  "access": "public"
104
+ },
105
+ "config": {
106
+ "commitizen": {
107
+ "path": "./node_modules/@ryansonshine/cz-conventional-changelog"
108
+ }
109
+ },
110
+ "lint-staged": {
111
+ "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": [
112
+ "biome check --files-ignore-unknown=true",
113
+ "biome check --write --no-errors-on-unmatched",
114
+ "biome check --write --organize-imports-enabled=false --no-errors-on-unmatched",
115
+ "biome check --write --unsafe --no-errors-on-unmatched",
116
+ "biome format --write --no-errors-on-unmatched",
117
+ "biome lint --write --no-errors-on-unmatched"
118
+ ]
89
119
  }
90
120
  }
package/src/index.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { Command } from 'commander';
4
- import { LIB_VERSION } from './version';
5
- import { generateToc, init, link, listAdrs, newAdr } from './lib/adr';
6
- import chalk from 'chalk';
7
- import { workingDir } from './lib/config';
8
3
  import * as path from 'path';
9
- import { getLinksFrom, getTitleFrom } from './lib/manipulator';
4
+ import chalk from 'chalk';
5
+ import { Command } from 'commander';
10
6
  import fs from 'fs/promises';
7
+ import { generateToc, init, link, listAdrs, newAdr } from './lib/adr.js';
8
+ import { workingDir } from './lib/config.js';
9
+ import { getLinksFrom, getTitleFrom } from './lib/manipulator.js';
10
+ import { LIB_VERSION } from './version.js';
11
11
 
12
12
  const program = new Command();
13
13
 
@@ -21,7 +21,7 @@ const collectSupersedes = (val: string, memo: string[]) => {
21
21
  return memo;
22
22
  };
23
23
 
24
- const generateGraph = async (options?: {prefix: string, extension :string}) => {
24
+ const generateGraph = async (options?: { prefix: string; extension: string }) => {
25
25
  let text = 'digraph {\n';
26
26
  text += ' node [shape=plaintext];\n';
27
27
  text += ' subgraph {\n';
@@ -49,7 +49,6 @@ const generateGraph = async (options?: {prefix: string, extension :string}) => {
49
49
  text += ` _${n} -> _${linksInADR[j].targetNumber} [label="${linksInADR[j].label}", weight=0]\n`;
50
50
  }
51
51
  }
52
-
53
52
  }
54
53
 
55
54
  text += '}\n';
@@ -58,16 +57,30 @@ const generateGraph = async (options?: {prefix: string, extension :string}) => {
58
57
 
59
58
  program.name('adr').version(LIB_VERSION).description('Manage Architecture Decision Logs');
60
59
 
61
- program.command('new')
60
+ program
61
+ .command('new')
62
62
  .argument('<title...>', 'The title of the decision')
63
- .option('-q, --quiet', 'Do not ask for clarification. If multiple files match the search pattern, an error will be thrown.')
64
- .option('-s, --supersede <SUPERSEDE>', 'A reference (number or partial filename) of a previous decision that the new decision supercedes.\n'
65
- + 'A Markdown link to the superceded ADR is inserted into the Status section.\n'
66
- + 'The status of the superceded ADR is changed to record that it has been superceded by the new ADR.', collectSupersedes, [])
67
- .option('-l, --link "<TARGET:LINK:REVERSE-LINK>"', 'Links the new ADR to a previous ADR.\n'
68
- + `${chalk.bold('TARGET')} is a reference (number or partial filename) of a previous decision.\n`
69
- + `${chalk.bold('LINK')} is the description of the link created in the new ADR.\n`
70
- + `${chalk.bold('REVERSE-LINK')} is the description of the link created in the existing ADR that will refer to the new ADR`, collectLinks, [])
63
+ .option(
64
+ '-q, --quiet',
65
+ 'Do not ask for clarification. If multiple files match the search pattern, an error will be thrown.'
66
+ )
67
+ .option(
68
+ '-s, --supersede <SUPERSEDE>',
69
+ 'A reference (number or partial filename) of a previous decision that the new decision supercedes.\n' +
70
+ 'A Markdown link to the superceded ADR is inserted into the Status section.\n' +
71
+ 'The status of the superceded ADR is changed to record that it has been superceded by the new ADR.',
72
+ collectSupersedes,
73
+ []
74
+ )
75
+ .option(
76
+ '-l, --link "<TARGET:LINK:REVERSE-LINK>"',
77
+ 'Links the new ADR to a previous ADR.\n' +
78
+ `${chalk.bold('TARGET')} is a reference (number or partial filename) of a previous decision.\n` +
79
+ `${chalk.bold('LINK')} is the description of the link created in the new ADR.\n` +
80
+ `${chalk.bold('REVERSE-LINK')} is the description of the link created in the existing ADR that will refer to the new ADR`,
81
+ collectLinks,
82
+ []
83
+ )
71
84
  .action(async (title: string[], options) => {
72
85
  try {
73
86
  await newAdr(title.join(' '), {
@@ -83,33 +96,45 @@ program.command('new')
83
96
 
84
97
  const generate = program.command('generate');
85
98
 
86
- generate.command('toc')
99
+ generate
100
+ .command('toc')
87
101
  .option('-p, --prefix <PREFIX>', 'The prefix to use for each file link in the generated TOC.')
88
102
  .action((options) => generateToc(options));
89
103
 
90
- generate.command('graph')
104
+ generate
105
+ .command('graph')
91
106
  .option('-p, --prefix <PREFIX>', 'Prefix each decision file link with PREFIX.')
92
- .option('-e, --extension <EXTENSION>', 'the file extension of the documents to which generated links refer. Defaults to .html', '.html')
107
+ .option(
108
+ '-e, --extension <EXTENSION>',
109
+ 'the file extension of the documents to which generated links refer. Defaults to .html',
110
+ '.html'
111
+ )
93
112
  .action(async (options) => {
94
113
  await generateGraph(options);
95
114
  });
96
115
 
97
- program.command('link')
116
+ program
117
+ .command('link')
98
118
  .argument('<SOURCE>', 'Full or Partial reference number to an ADR')
99
119
  .argument('<LINK>', 'The description of the link created in the SOURCE')
100
120
  .argument('<TARGET>', 'Full or Partial reference number to an ADR')
101
121
  .argument('<REVERSE-LINK>', 'The description of the link created in the TARGET')
102
- .option('-q, --quiet', 'Do not ask for clarification. If multiple files match the search pattern, an error will be thrown.')
122
+ .option(
123
+ '-q, --quiet',
124
+ 'Do not ask for clarification. If multiple files match the search pattern, an error will be thrown.'
125
+ )
103
126
  .action(link);
104
127
 
105
- program.command('init').argument('[directory]', 'Initialize a new ADR directory').action(async (directory?: string) => {
106
- await init(directory);
107
- });
128
+ program
129
+ .command('init')
130
+ .argument('[directory]', 'Initialize a new ADR directory')
131
+ .action(async (directory?: string) => {
132
+ await init(directory);
133
+ });
108
134
 
109
135
  program.command('list').action(async () => {
110
136
  const adrs = await listAdrs();
111
- console.log(adrs.map(adr => path.relative(workingDir(), adr)).join('\n'));
137
+ console.log(adrs.map((adr) => path.relative(workingDir(), adr)).join('\n'));
112
138
  });
113
139
 
114
140
  program.parse();
115
-
package/src/lib/adr.ts CHANGED
@@ -1,13 +1,15 @@
1
- import { newNumber } from './numbering';
2
- import { template } from './template';
3
- import fs from 'fs/promises';
4
- import path from 'path';
5
- import { getDir, workingDir } from './config';
6
- import { prompt } from 'inquirer';
7
- import chalk from 'chalk';
8
- import { getTitleFrom, injectLink, supersede } from './manipulator';
9
- import { findMatchingFilesFor, getLinkDetails } from './links';
10
1
  import childProcess from 'node:child_process';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import fs from 'fs/promises';
5
+ import { getDir, workingDir } from './config.js';
6
+ import { findMatchingFilesFor, getLinkDetails } from './links.js';
7
+ import { getTitleFrom, injectLink, supersede } from './manipulator.js';
8
+ import { newNumber } from './numbering.js';
9
+ import { askForClarification } from './prompt.js';
10
+ import { template } from './template.js';
11
+
12
+ const __filename = fileURLToPath(import.meta.url);
11
13
 
12
14
  interface NewOptions {
13
15
  supersedes?: string[];
@@ -17,18 +19,6 @@ interface NewOptions {
17
19
  links?: string[];
18
20
  }
19
21
 
20
- const askForClarification = async (searchString: string, matches: string[]) => {
21
- const selection = await prompt([
22
- {
23
- type: 'list',
24
- name: 'target',
25
- message: `Which file do you want to link to for ${chalk.blue(searchString)}?`,
26
- choices: matches
27
- }
28
- ]);
29
- return selection.target;
30
- };
31
-
32
22
  // eslint-disable-next-line no-unused-vars
33
23
  enum LinkType {
34
24
  // eslint-disable-next-line no-unused-vars
@@ -51,14 +41,21 @@ const actuallyLink = async (task: LinkTask) => {
51
41
  const oldAdrContent = await fs.readFile(linkedFile, 'utf8');
52
42
  const oldTitle = getTitleFrom(oldAdrContent);
53
43
  const newTitle = getTitleFrom(newAdrContent);
54
- let dirtyOld = '', dirtyNew = '';
44
+ let dirtyOld = '',
45
+ dirtyNew = '';
55
46
  switch (task.type) {
56
47
  case LinkType.LINK:
57
- dirtyOld = injectLink(oldAdrContent, `${task.reverseLink} [${newTitle}](${path.relative(await getDir(), task.sourcePath)})`);
48
+ dirtyOld = injectLink(
49
+ oldAdrContent,
50
+ `${task.reverseLink} [${newTitle}](${path.relative(await getDir(), task.sourcePath)})`
51
+ );
58
52
  dirtyNew = injectLink(newAdrContent, `${task.link} [${oldTitle}](${task.targetPath})`);
59
53
  break;
60
54
  case LinkType.SUPERSEDE:
61
- dirtyOld = supersede(oldAdrContent, `${task.reverseLink} [${newTitle}](${path.relative(await getDir(), task.sourcePath)})`);
55
+ dirtyOld = supersede(
56
+ oldAdrContent,
57
+ `${task.reverseLink} [${newTitle}](${path.relative(await getDir(), task.sourcePath)})`
58
+ );
62
59
  dirtyNew = injectLink(newAdrContent, `${task.link} [${oldTitle}](${task.targetPath})`);
63
60
  break;
64
61
  default:
@@ -69,11 +66,7 @@ const actuallyLink = async (task: LinkTask) => {
69
66
  await fs.writeFile(task.sourcePath, dirtyNew);
70
67
  };
71
68
 
72
- const processSupersedes = async (
73
- sourcePath: string,
74
- supersedes: string[] = [],
75
- suppressPrompts: boolean = false
76
- ) => {
69
+ const processSupersedes = async (sourcePath: string, supersedes: string[] = [], suppressPrompts: boolean = false) => {
77
70
  if (supersedes.length === 0) {
78
71
  return;
79
72
  }
@@ -91,22 +84,19 @@ const processSupersedes = async (
91
84
 
92
85
  if (targetDetails.matches.length > 1) {
93
86
  if (suppressPrompts) {
94
- throw new Error(`Multiple files match the search pattern for "${targetDetails.original}".\n`
95
- + 'Please specify which file you want to targetDetails to more or remove the -q or --quiet options from the command line.');
96
- } else {
97
- task.targetPath = await askForClarification(targetDetails.original, targetDetails.matches);
87
+ throw new Error(
88
+ `Multiple files match the search pattern for "${targetDetails.original}".\n` +
89
+ 'Please specify which file you want to targetDetails to more or remove the -q or --quiet options from the command line.'
90
+ );
98
91
  }
92
+ task.targetPath = await askForClarification(targetDetails.original, targetDetails.matches);
99
93
  }
100
94
 
101
95
  await actuallyLink(task);
102
96
  }
103
97
  };
104
98
 
105
- const injectLinksTo = async (
106
- sourcePath: string,
107
- links: string[] = [],
108
- suppressPrompts: boolean = false
109
- ) => {
99
+ const injectLinksTo = async (sourcePath: string, links: string[] = [], suppressPrompts: boolean = false) => {
110
100
  if (links.length === 0) {
111
101
  return;
112
102
  }
@@ -124,20 +114,19 @@ const injectLinksTo = async (
124
114
 
125
115
  if (targetDetails.matches.length > 1) {
126
116
  if (suppressPrompts) {
127
- throw new Error(`Multiple files match the search pattern for "${targetDetails.original}".\n`
128
- + 'Please specify which file you want to targetDetails to more or remove the -q or --quiet options from the command line.');
129
- } else {
130
- task.targetPath = await askForClarification(targetDetails.original, targetDetails.matches);
117
+ throw new Error(
118
+ `Multiple files match the search pattern for "${targetDetails.original}".\n` +
119
+ 'Please specify which file you want to targetDetails to more or remove the -q or --quiet options from the command line.'
120
+ );
131
121
  }
122
+ task.targetPath = await askForClarification(targetDetails.original, targetDetails.matches);
132
123
  }
133
124
 
134
125
  await actuallyLink(task);
135
126
  }
136
-
137
127
  };
138
128
  //Generate a table of contents for the adr directory
139
- export const generateToc = async (options?: {prefix?: string}) => {
140
-
129
+ export const generateToc = async (options?: { prefix?: string }) => {
141
130
  const adrDir = await getDir();
142
131
  const files = await fs.readdir(adrDir);
143
132
  const toc = files.filter((file) => file.match(/^\d{4}-.*\.md$/));
@@ -157,27 +146,26 @@ export const newAdr = async (title: string, config?: NewOptions) => {
157
146
  const newNum = await newNumber();
158
147
  const formattedDate = config?.date || new Date().toISOString().split('T')[0] || 'ERROR';
159
148
  const tpl = await template(config?.template);
160
- const adr = tpl.replace('DATE', formattedDate).replace('TITLE', title).replace('NUMBER', newNum.toString()).replace('STATUS', 'Accepted');
149
+ const adr = tpl
150
+ .replace('DATE', formattedDate)
151
+ .replace('TITLE', title)
152
+ .replace('NUMBER', newNum.toString())
153
+ .replace('STATUS', 'Accepted');
161
154
  const paddedNumber = newNum.toString().padStart(4, '0');
162
- const cleansedTitle = title.toLowerCase().replace(/\W/g, '-').replace(/^(.*)\W$/, '$1').replace(/^\W(.*)$/, '$1');
155
+ const cleansedTitle = title
156
+ .toLowerCase()
157
+ .replace(/\W/g, '-')
158
+ .replace(/^(.*)\W$/, '$1')
159
+ .replace(/^\W(.*)$/, '$1');
163
160
  const fileName = `${paddedNumber}-${cleansedTitle}.md`;
164
161
  const adrDirectory = await getDir();
165
162
  const adrPath = path.resolve(path.join(adrDirectory, fileName));
166
163
  await fs.writeFile(adrPath, adr);
167
164
  await fs.writeFile(path.resolve(adrDirectory, '.adr-sequence.lock'), newNum.toString());
168
165
 
169
- await processSupersedes(
170
- adrPath,
171
- config?.supersedes,
172
- config?.suppressPrompts
173
- );
174
- await injectLinksTo(
175
- adrPath,
176
- config?.links,
177
- config?.suppressPrompts
178
- );
166
+ await processSupersedes(adrPath, config?.supersedes, config?.suppressPrompts);
167
+ await injectLinksTo(adrPath, config?.links, config?.suppressPrompts);
179
168
  await generateToc();
180
- const newAdrPath = path.relative(workingDir(), adrPath);
181
169
 
182
170
  if (process.env.VISUAL) {
183
171
  await childProcess.spawn(process.env.VISUAL, [adrPath], {
@@ -193,12 +181,10 @@ export const newAdr = async (title: string, config?: NewOptions) => {
193
181
  });
194
182
  return;
195
183
  }
196
- console.log(newAdrPath);
197
-
198
184
  };
199
185
 
200
186
  export const init = async (directory?: string) => {
201
- const dir = directory || await getDir();
187
+ const dir = directory || (await getDir());
202
188
  await fs.mkdir(dir, { recursive: true });
203
189
  await fs.writeFile(path.join(workingDir(), '.adr-dir'), path.relative(workingDir(), dir));
204
190
  await newAdr('Record Architecture Decisions', {
@@ -207,7 +193,13 @@ export const init = async (directory?: string) => {
207
193
  });
208
194
  };
209
195
 
210
- export const link = async (source: string, link: string, target: string, reverseLink: string, options?: {quiet?: boolean}) => {
196
+ export const link = async (
197
+ source: string,
198
+ link: string,
199
+ target: string,
200
+ reverseLink: string,
201
+ options?: { quiet?: boolean }
202
+ ) => {
211
203
  const getFor = async (pattern: string) => {
212
204
  const found = await findMatchingFilesFor(pattern);
213
205
  if (found.length === 1) {
@@ -215,8 +207,10 @@ export const link = async (source: string, link: string, target: string, reverse
215
207
  }
216
208
 
217
209
  if (options?.quiet) {
218
- throw new Error(`Multiple files match the search pattern for "${pattern}".\n`
219
- + 'Please specify which file you want to targetDetails to more or remove the -q or --quiet options from the command line.');
210
+ throw new Error(
211
+ `Multiple files match the search pattern for "${pattern}".\n` +
212
+ 'Please specify which file you want to targetDetails to more or remove the -q or --quiet options from the command line.'
213
+ );
220
214
  }
221
215
 
222
216
  return askForClarification(pattern, found);
@@ -225,18 +219,19 @@ export const link = async (source: string, link: string, target: string, reverse
225
219
  const sourceFile = await getFor(source);
226
220
 
227
221
  await injectLinksTo(path.resolve(await getDir(), sourceFile), [`${target}:${link}:${reverseLink}`]);
228
-
229
222
  };
230
223
 
231
224
  export const listAdrs = async () => {
232
225
  const dir = await getDir();
233
226
  const files = await fs.readdir(dir);
234
- const toc = files.map(f => {
235
- const adrFile = f.match(/^0*(\d+)-.*$/);
236
- if (adrFile) {
237
- return path.resolve(dir, adrFile[0]);
238
- }
239
- return '';
240
- }).filter(f => f !== '');
227
+ const toc = files
228
+ .map((f) => {
229
+ const adrFile = f.match(/^0*(\d+)-.*$/);
230
+ if (adrFile) {
231
+ return path.resolve(dir, adrFile[0]);
232
+ }
233
+ return '';
234
+ })
235
+ .filter((f) => f !== '');
241
236
  return toc;
242
237
  };
package/src/lib/config.ts CHANGED
@@ -1,6 +1,6 @@
1
- import fs from 'fs/promises';
2
1
  import { constants } from 'fs';
3
2
  import path from 'path';
3
+ import fs from 'fs/promises';
4
4
 
5
5
  export const workingDir = () => process.cwd();
6
6
 
@@ -8,7 +8,7 @@ const findTopLevelDir = async (dir: string): Promise<string> => {
8
8
  try {
9
9
  await fs.access(path.join(dir, '.adr-dir'), constants.F_OK);
10
10
  return dir;
11
- } catch (e) {
11
+ } catch (_e) {
12
12
  if (dir === '/') {
13
13
  throw new Error('No ADR directory config found');
14
14
  }
@@ -22,7 +22,7 @@ const getDirPath = async (): Promise<string> => {
22
22
  const configDir = await findTopLevelDir(workingDir());
23
23
  const configFile = await fs.readFile(path.join(configDir, '.adr-dir'), 'utf8');
24
24
  return path.relative(workingDir(), path.join(configDir, configFile.trim()));
25
- } catch (e) {
25
+ } catch (_e) {
26
26
  return path.resolve(path.join(workingDir(), 'doc/adr'));
27
27
  }
28
28
  };
@@ -1,9 +1,9 @@
1
- import { vi, describe, it, expect, afterEach } from 'vitest';
2
1
  import fs from 'fs/promises';
3
- import { getDir } from './config';
4
- import { getLinkDetails } from './links';
2
+ import { afterEach, describe, expect, it, vi } from 'vitest';
3
+ import { getDir } from './config.js';
4
+ import { getLinkDetails } from './links.js';
5
5
 
6
- vi.mock('./config');
6
+ vi.mock('./config.js');
7
7
  vi.mock('fs/promises');
8
8
 
9
9
  describe('The link lib', () => {
@@ -27,11 +27,7 @@ describe('The link lib', () => {
27
27
 
28
28
  it('does handles multiple matches', async () => {
29
29
  vi.mocked(getDir).mockResolvedValueOnce('/');
30
- vi.mocked(fs.readdir).mockResolvedValueOnce([
31
- '1-one',
32
- '1-two',
33
- '1-three'
34
- ] as any);
30
+ vi.mocked(fs.readdir).mockResolvedValueOnce(['1-one', '1-two', '1-three'] as any);
35
31
  const linkString = '1:overrides:overriden by';
36
32
  const response = await getLinkDetails(linkString);
37
33
  expect(response).toEqual({
@@ -39,21 +35,13 @@ describe('The link lib', () => {
39
35
  original: '1:overrides:overriden by',
40
36
  link: 'overrides',
41
37
  reverseLink: 'overriden by',
42
- matches: [
43
- '1-one',
44
- '1-two',
45
- '1-three'
46
- ]
38
+ matches: ['1-one', '1-two', '1-three']
47
39
  });
48
40
  });
49
41
 
50
42
  it('returns only files that match the pattern', async () => {
51
43
  vi.mocked(getDir).mockResolvedValueOnce('/');
52
- vi.mocked(fs.readdir).mockResolvedValueOnce([
53
- 'on1e',
54
- '1-two',
55
- 'three'
56
- ] as any);
44
+ vi.mocked(fs.readdir).mockResolvedValueOnce(['on1e', '1-two', 'three'] as any);
57
45
  const linkString = '1:overrides:overriden by';
58
46
  const response = await getLinkDetails(linkString);
59
47
  expect(response).toEqual({
@@ -61,10 +49,7 @@ describe('The link lib', () => {
61
49
  original: '1:overrides:overriden by',
62
50
  link: 'overrides',
63
51
  reverseLink: 'overriden by',
64
- matches: [
65
- 'on1e',
66
- '1-two'
67
- ]
52
+ matches: ['on1e', '1-two']
68
53
  });
69
54
  });
70
55
 
@@ -82,5 +67,4 @@ describe('The link lib', () => {
82
67
  matches: []
83
68
  });
84
69
  });
85
-
86
70
  });
package/src/lib/links.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import fs from 'fs/promises';
2
- import { getDir } from './config';
2
+ import { getDir } from './config.js';
3
3
 
4
4
  export interface LinkDetails {
5
5
  pattern: string;
@@ -11,7 +11,7 @@ export interface LinkDetails {
11
11
 
12
12
  export const findMatchingFilesFor = async (pattern: string) => {
13
13
  const files = await fs.readdir(await getDir());
14
- return files.filter(file => file.includes(pattern));
14
+ return files.filter((file) => file.includes(pattern));
15
15
  };
16
16
 
17
17
  export const getLinkDetails = async (linkString: string, isSupersede: boolean = false): Promise<LinkDetails> => {