diff --git a/package.json b/package.json index 11ce7b7..4d74d37 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,8 @@ "release": "tnode scripts/release.ts" }, "dependencies": { + "conventional-changelog": "^7.1.0", + "conventional-changelog-conventionalcommits": "^9.0.0", "execa": "^8.0.1", "mri": "^1.2.0", "picocolors": "^1.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 91d47c0..759268a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,12 @@ importers: .: dependencies: + conventional-changelog: + specifier: ^7.1.0 + version: 7.1.0(conventional-commits-filter@5.0.0) + conventional-changelog-conventionalcommits: + specifier: ^9.0.0 + version: 9.0.0 execa: specifier: ^8.0.1 version: 8.0.1 @@ -55,6 +61,18 @@ packages: resolution: {integrity: sha512-biE/vNihcZJ3lfXzLaYWaJoaUIE2hLEriCUzuMB9esRAMBIK4eqxnWpVIn0vKxqBWwOIEhrvdOGXeBEr+fyoEg==} hasBin: true + '@conventional-changelog/git-client@2.5.1': + resolution: {integrity: sha512-lAw7iA5oTPWOLjiweb7DlGEMDEvzqzLLa6aWOly2FSZ64IwLE8T458rC+o+WvI31Doz6joM7X2DoNog7mX8r4A==} + engines: {node: '>=18'} + peerDependencies: + conventional-commits-filter: ^5.0.0 + conventional-commits-parser: ^6.1.0 + peerDependenciesMeta: + conventional-commits-filter: + optional: true + conventional-commits-parser: + optional: true + '@esbuild/aix-ppc64@0.25.5': resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} engines: {node: '>=18'} @@ -209,19 +227,67 @@ packages: resolution: {integrity: sha512-S+9ANAvUmjutrshV4jZjaiG8XQyuJIZ8a4utWmN/vW1sgQ9IfBnPndwkmQYw53QmouOIytT874u65HEmu6H5jw==} engines: {node: '>=18'} + '@simple-libs/child-process-utils@1.0.1': + resolution: {integrity: sha512-3nWd8irxvDI6v856wpPCHZ+08iQR0oHTZfzAZmnbsLzf+Sf1odraP6uKOHDZToXq3RPRV/LbqGVlSCogm9cJjg==} + engines: {node: '>=18'} + + '@simple-libs/stream-utils@1.1.0': + resolution: {integrity: sha512-6rsHTjodIn/t90lv5snQjRPVtOosM7Vp0AKdrObymq45ojlgVwnpAqdc+0OBBrpEiy31zZ6/TKeIVqV1HwvnuQ==} + engines: {node: '>=18'} + '@types/node@22.15.33': resolution: {integrity: sha512-wzoocdnnpSxZ+6CjW4ADCK1jVmd1S/J3ArNWfn8FDDQtRm8dkDg7TA+mvek2wNrfCgwuZxqEOiB9B1XCJ6+dbw==} + '@types/normalize-package-data@2.4.4': + resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + '@types/prompts@2.4.9': resolution: {integrity: sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==} '@types/semver@7.7.0': resolution: {integrity: sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==} + array-ify@1.0.0: + resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + + compare-func@2.0.0: + resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + + conventional-changelog-conventionalcommits@9.0.0: + resolution: {integrity: sha512-5e48V0+DsWvQBEnnbBFhYQwYDzFPXVrakGPP1uSxekDkr5d7YWrmaWsgJpKFR0SkXmxK6qQr9O42uuLb9wpKxA==} + engines: {node: '>=18'} + + conventional-changelog-preset-loader@5.0.0: + resolution: {integrity: sha512-SetDSntXLk8Jh1NOAl1Gu5uLiCNSYenB5tm0YVeZKePRIgDW9lQImromTwLa3c/Gae298tsgOM+/CYT9XAl0NA==} + engines: {node: '>=18'} + + conventional-changelog-writer@8.1.0: + resolution: {integrity: sha512-dpC440QnORNCO81XYuRRFOLCsjKj4W7tMkUIn3lR6F/FAaJcWLi7iCj6IcEvSQY2zw6VUgwUKd5DEHKEWrpmEQ==} + engines: {node: '>=18'} + hasBin: true + + conventional-changelog@7.1.0: + resolution: {integrity: sha512-2hHa/MpDunPnYK3QcZdHl4MOnyLlicBmohsM5/dfvfeoPp0faIjYKHbKyb8nKth/Zd4HhxtlMWfVKrmM9OMj/Q==} + engines: {node: '>=18'} + hasBin: true + + conventional-commits-filter@5.0.0: + resolution: {integrity: sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==} + engines: {node: '>=18'} + + conventional-commits-parser@6.2.0: + resolution: {integrity: sha512-uLnoLeIW4XaoFtH37qEcg/SXMJmKF4vi7V0H2rnPueg+VEtFGA/asSCNTcq4M/GQ6QmlzchAEtOoDTtKqWeHag==} + engines: {node: '>=18'} + hasBin: true + cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + dot-prop@5.3.0: + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} + esbuild@0.25.5: resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} engines: {node: '>=18'} @@ -231,14 +297,30 @@ packages: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} + fd-package-json@1.2.0: + resolution: {integrity: sha512-45LSPmWf+gC5tdCQMNH4s9Sr00bIkiD9aN7dc5hqkrEw1geRYyDQS1v1oMHAW3ysfxfndqGsrDREHHjNNbKUfA==} + get-stream@8.0.1: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} + handlebars@4.7.8: + resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + engines: {node: '>=0.4.7'} + hasBin: true + + hosted-git-info@8.1.0: + resolution: {integrity: sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==} + engines: {node: ^18.17.0 || >=20.5.0} + human-signals@5.0.0: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} + is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + is-stream@3.0.0: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -250,6 +332,13 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + meow@13.2.0: + resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} + engines: {node: '>=18'} + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -257,10 +346,20 @@ packages: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + normalize-package-data@7.0.0: + resolution: {integrity: sha512-k6U0gKRIuNCTkwHGZqblCfLfBRh+w1vI6tBo+IeJwq2M8FUiOqhX7GH+GArQGScA7azd1WfyRCvxoXDO3hQDIA==} + engines: {node: ^18.17.0 || >=20.5.0} + npm-run-path@5.1.0: resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -321,6 +420,22 @@ packages: sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-license-ids@3.0.21: + resolution: {integrity: sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==} + strip-final-newline@3.0.0: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} engines: {node: '>=12'} @@ -330,20 +445,43 @@ packages: engines: {node: '>=14.17'} hasBin: true + uglify-js@3.19.3: + resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} + engines: {node: '>=0.8.0'} + hasBin: true + undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + + walk-up-path@3.0.1: + resolution: {integrity: sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true + wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + snapshots: '@arnaud-barre/tnode@0.25.0': dependencies: esbuild: 0.25.5 + '@conventional-changelog/git-client@2.5.1(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.2.0)': + dependencies: + '@simple-libs/child-process-utils': 1.0.1 + '@simple-libs/stream-utils': 1.1.0 + semver: 7.7.2 + optionalDependencies: + conventional-commits-filter: 5.0.0 + conventional-commits-parser: 6.2.0 + '@esbuild/aix-ppc64@0.25.5': optional: true @@ -421,10 +559,21 @@ snapshots: '@publint/pack@0.1.2': {} + '@simple-libs/child-process-utils@1.0.1': + dependencies: + '@simple-libs/stream-utils': 1.1.0 + '@types/node': 22.15.33 + + '@simple-libs/stream-utils@1.1.0': + dependencies: + '@types/node': 22.15.33 + '@types/node@22.15.33': dependencies: undici-types: 6.21.0 + '@types/normalize-package-data@2.4.4': {} + '@types/prompts@2.4.9': dependencies: '@types/node': 22.15.33 @@ -432,12 +581,55 @@ snapshots: '@types/semver@7.7.0': {} + array-ify@1.0.0: {} + + compare-func@2.0.0: + dependencies: + array-ify: 1.0.0 + dot-prop: 5.3.0 + + conventional-changelog-conventionalcommits@9.0.0: + dependencies: + compare-func: 2.0.0 + + conventional-changelog-preset-loader@5.0.0: {} + + conventional-changelog-writer@8.1.0: + dependencies: + conventional-commits-filter: 5.0.0 + handlebars: 4.7.8 + meow: 13.2.0 + semver: 7.7.2 + + conventional-changelog@7.1.0(conventional-commits-filter@5.0.0): + dependencies: + '@conventional-changelog/git-client': 2.5.1(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.2.0) + '@types/normalize-package-data': 2.4.4 + conventional-changelog-preset-loader: 5.0.0 + conventional-changelog-writer: 8.1.0 + conventional-commits-parser: 6.2.0 + fd-package-json: 1.2.0 + meow: 13.2.0 + normalize-package-data: 7.0.0 + transitivePeerDependencies: + - conventional-commits-filter + + conventional-commits-filter@5.0.0: {} + + conventional-commits-parser@6.2.0: + dependencies: + meow: 13.2.0 + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 + dot-prop@5.3.0: + dependencies: + is-obj: 2.0.0 + esbuild@0.25.5: optionalDependencies: '@esbuild/aix-ppc64': 0.25.5 @@ -478,22 +670,55 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 + fd-package-json@1.2.0: + dependencies: + walk-up-path: 3.0.1 + get-stream@8.0.1: {} + handlebars@4.7.8: + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.19.3 + + hosted-git-info@8.1.0: + dependencies: + lru-cache: 10.4.3 + human-signals@5.0.0: {} + is-obj@2.0.0: {} + is-stream@3.0.0: {} isexe@2.0.0: {} kleur@3.0.3: {} + lru-cache@10.4.3: {} + + meow@13.2.0: {} + merge-stream@2.0.0: {} mimic-fn@4.0.0: {} + minimist@1.2.8: {} + mri@1.2.0: {} + neo-async@2.6.2: {} + + normalize-package-data@7.0.0: + dependencies: + hosted-git-info: 8.1.0 + semver: 7.7.2 + validate-npm-package-license: 3.0.4 + npm-run-path@5.1.0: dependencies: path-key: 4.0.0 @@ -540,12 +765,40 @@ snapshots: sisteransi@1.0.5: {} + source-map@0.6.1: {} + + spdx-correct@3.2.0: + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.21 + + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@3.0.1: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.21 + + spdx-license-ids@3.0.21: {} + strip-final-newline@3.0.0: {} typescript@5.8.3: {} + uglify-js@3.19.3: + optional: true + undici-types@6.21.0: {} + validate-npm-package-license@3.0.4: + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + + walk-up-path@3.0.1: {} + which@2.0.2: dependencies: isexe: 2.0.0 + + wordwrap@1.0.0: {} diff --git a/src/changelog.ts b/src/changelog.ts new file mode 100644 index 0000000..8e20d63 --- /dev/null +++ b/src/changelog.ts @@ -0,0 +1,62 @@ +import fs from "node:fs"; +import { ConventionalChangelog, type Preset } from "conventional-changelog"; +import createPreset, { + DEFAULT_COMMIT_TYPES, + // @ts-expect-error no types +} from "conventional-changelog-conventionalcommits"; +import type { generateChangelog as def } from "./types.d.ts"; + +export const generateChangelog: typeof def = async ({ + getPkgDir, + tagPrefix, +}) => { + const preset: Preset = await createPreset({ + types: DEFAULT_COMMIT_TYPES.map((t: any) => ({ ...t, hidden: false })), + }); + preset.writer ??= {}; + preset.writer.headerPartial = ` +## {{#if isPatch~}} {{~/if~}} +{{#if @root.linkCompare~}} +[{{version}}]( +{{~#if @root.repository~}} + {{~#if @root.host}} + {{~@root.host}}/ + {{~/if}} + {{~#if @root.owner}} + {{~@root.owner}}/ + {{~/if}} + {{~@root.repository}} +{{~else}} + {{~@root.repoUrl}} +{{~/if~}} +/compare/{{previousTag}}...{{currentTag}}) +{{~else}} +{{~version}} +{{~/if}} +{{~#if title}} "{{title}}" +{{~/if}} +{{~#if date}} ({{date}}) +{{~/if}} +{{~#if isPatch~}} {{~/if}} +`.trim(); + preset.writer.mainTemplate! += "\n"; + + const pkgDir = getPkgDir(); + + const generator = new ConventionalChangelog() + .readPackage(`${pkgDir}/package.json`) + .config(preset) + .options({ releaseCount: 1 }) + .commits({ path: pkgDir }); + if (tagPrefix) { + generator.tags({ prefix: tagPrefix }); + } + + const originalChangelog = fs.readFileSync(`${pkgDir}/CHANGELOG.md`, "utf-8"); + + const writeStream = fs.createWriteStream(`${pkgDir}/CHANGELOG.md`); + for await (const chunk of generator.write()) { + writeStream.write(chunk); + } + writeStream.write(originalChangelog); +}; diff --git a/src/index.ts b/src/index.ts index 997d733..a1dbea5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,2 +1,3 @@ export { publish } from "./publish.ts"; export { release } from "./release.ts"; +export { generateChangelog } from "./changelog.ts"; diff --git a/src/types.d.ts b/src/types.d.ts index 7320296..65008ce 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -1,3 +1,10 @@ +export declare function generateChangelog(options: { + /** @example () => `packages/${pkgName}` */ + getPkgDir: () => string; + /** @example `${pkgName}@` */ + tagPrefix?: string; +}): Promise; + export declare function publish(options: { defaultPackage?: string; getPkgDir?: (pkg: string) => string;