Compare commits
2 Commits
7336efacbb
...
0ed4ca9cab
Author | SHA1 | Date | |
---|---|---|---|
0ed4ca9cab | |||
01e5381b77 |
@ -1,12 +1,16 @@
|
|||||||
project:
|
project:
|
||||||
name: null
|
name: changelord
|
||||||
homepage: null
|
homepage: https://git.babyl.ca/yanick/changelord.js
|
||||||
with_stats: true
|
with_stats: true
|
||||||
ticket_url: null
|
ticket_url: null
|
||||||
releases:
|
releases:
|
||||||
- version: NEXT
|
- version: NEXT
|
||||||
changes: []
|
changes:
|
||||||
|
- port the Perl changelord to JavaScript.
|
||||||
change_types:
|
change_types:
|
||||||
|
- title: ''
|
||||||
|
level: minor
|
||||||
|
keywords: ['']
|
||||||
- title: Features
|
- title: Features
|
||||||
level: minor
|
level: minor
|
||||||
keywords:
|
keywords:
|
||||||
|
6
Taskfile.yaml
Normal file
6
Taskfile.yaml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# https://taskfile.dev
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
test: vitest run src
|
||||||
|
test:dev: vitest src
|
@ -18,10 +18,13 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"consola": "^3.1.0",
|
"consola": "^3.1.0",
|
||||||
"fs-extra": "^11.1.1",
|
"fs-extra": "^11.1.1",
|
||||||
|
"markdown-utils": "^1.0.0",
|
||||||
|
"remeda": "^1.14.0",
|
||||||
"yaml": "^2.2.2",
|
"yaml": "^2.2.2",
|
||||||
"yargs": "^17.7.2"
|
"yargs": "^17.7.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prettier": "^2.8.8"
|
"prettier": "^2.8.8",
|
||||||
|
"vitest": "^0.31.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
9
prettier.config.mjs
Normal file
9
prettier.config.mjs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export default {
|
||||||
|
endOfLine: "lf",
|
||||||
|
semi: true,
|
||||||
|
singleQuote: false,
|
||||||
|
tabWidth: 2,
|
||||||
|
trailingComma: "es5",
|
||||||
|
bracketSpacing: true,
|
||||||
|
proseWrap: "always",
|
||||||
|
};
|
@ -3,6 +3,8 @@
|
|||||||
import { hideBin } from 'yargs/helpers';
|
import { hideBin } from 'yargs/helpers';
|
||||||
import yargs from 'yargs';
|
import yargs from 'yargs';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
import yaml from 'yaml';
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
|
||||||
import print from './command/print.js'
|
import print from './command/print.js'
|
||||||
import init from './command/init.js'
|
import init from './command/init.js'
|
||||||
@ -11,9 +13,10 @@ import consola from 'consola';
|
|||||||
|
|
||||||
consola.raw = (...args) => console.log(...args);
|
consola.raw = (...args) => console.log(...args);
|
||||||
|
|
||||||
|
|
||||||
yargs(hideBin(process.argv))
|
yargs(hideBin(process.argv))
|
||||||
.config({
|
.config({
|
||||||
consola
|
consola,
|
||||||
})
|
})
|
||||||
.default('source', join( process.cwd(), 'CHANGELOG.yml' ))
|
.default('source', join( process.cwd(), 'CHANGELOG.yml' ))
|
||||||
.describe('source', 'changelog source')
|
.describe('source', 'changelog source')
|
||||||
|
@ -1,50 +1,52 @@
|
|||||||
import fs from 'fs-extra';
|
import fs from "fs-extra";
|
||||||
import { consola } from 'consola';
|
import { consola } from "consola";
|
||||||
import { stringify } from 'yaml';
|
import { stringify } from "yaml";
|
||||||
|
|
||||||
const change_types = [
|
const change_types = [
|
||||||
{ title: 'Features' , level: 'minor', keywords: [ 'feat' ] } ,
|
{ title: "", level: "minor", keywords: [""] },
|
||||||
{ title : 'Bug fixes' , level : 'patch', keywords : [ 'fix' ] },
|
{ title: "Features", level: "minor", keywords: ["feat"] },
|
||||||
{ title : 'Package maintenance' , level : 'patch', keywords : [ 'chore', 'maint', 'refactor' ] },
|
{ title: "Bug fixes", level: "patch", keywords: ["fix"] },
|
||||||
{ title : 'Statistics' , level : 'patch', keywords : [ 'stats' ] },
|
{
|
||||||
];
|
title: "Package maintenance",
|
||||||
|
level: "patch",
|
||||||
|
keywords: ["chore", "maint", "refactor"],
|
||||||
|
},
|
||||||
|
{ title: "Statistics", level: "patch", keywords: ["stats"] },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const base_changelog = {
|
||||||
const base_changelog = {
|
project: {
|
||||||
project: {
|
name: null,
|
||||||
name: null,
|
homepage: null,
|
||||||
homepage: null,
|
with_stats: true,
|
||||||
with_stats: true,
|
ticket_url: null,
|
||||||
ticket_url: null,
|
},
|
||||||
},
|
releases: [{ version: "NEXT", changes: [] }],
|
||||||
releases: [
|
change_types,
|
||||||
{ version: 'NEXT', changes: [] }
|
|
||||||
],
|
|
||||||
change_types,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handler = async (config) => {
|
const handler = async (config) => {
|
||||||
if( await fs.pathExists(config.source) ) {
|
if (await fs.pathExists(config.source)) {
|
||||||
consola.error(`${config.source} already exist, aborting.`);
|
consola.error(`${config.source} already exist, aborting.`);
|
||||||
process.exit();
|
process.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
consola.start(`creating ${config.source}...`);
|
consola.start(`creating ${config.source}...`);
|
||||||
|
|
||||||
await fs.writeFile( config.source, stringify(base_changelog) );
|
await fs.writeFile(config.source, stringify(base_changelog));
|
||||||
|
|
||||||
consola.success('done!');
|
|
||||||
|
|
||||||
|
consola.success("done!");
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
command: 'init',
|
command: "init",
|
||||||
desc : 'initialize new changelog source file',
|
desc: "initialize new changelog source file",
|
||||||
builder: (yargs) => {
|
builder: (yargs) => {
|
||||||
yargs.boolean('json')
|
yargs
|
||||||
.boolean('next')
|
.boolean("json")
|
||||||
.default('json',false)
|
.boolean("next")
|
||||||
.default('next',true);
|
.default("json", false)
|
||||||
},
|
.default("next", true);
|
||||||
handler,
|
},
|
||||||
}
|
handler,
|
||||||
|
};
|
||||||
|
@ -1,16 +1,131 @@
|
|||||||
|
import * as R from 'remeda';
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
import yaml from 'yaml';
|
||||||
|
import mkd from 'markdown-utils';
|
||||||
|
|
||||||
|
const render_header = (source ) => {
|
||||||
|
let header = '# Changelog';
|
||||||
|
|
||||||
|
let name = source.project?.name
|
||||||
|
|
||||||
|
const links = {};
|
||||||
|
|
||||||
|
if( source?.project?.homepage) {
|
||||||
|
links.homepage = source?.project?.homepage;
|
||||||
|
name = `[${name}][homepage]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(name) header += ` for ${name}`;
|
||||||
|
|
||||||
|
return { header, links }
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
function render_links(links) {
|
||||||
|
|
||||||
|
return Object.entries(links).map(
|
||||||
|
([name,url]) => ` [${name}]: ${url}`
|
||||||
|
).join( "\n")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const render_change = (config,source)=>(change) => {
|
||||||
|
let body = change.desc;
|
||||||
|
let links = {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
body, links
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const render_release = (config,source) => ( release ) => {
|
||||||
|
let links = {};
|
||||||
|
|
||||||
|
if( typeof release === 'string') return {
|
||||||
|
body: release,
|
||||||
|
links
|
||||||
|
};
|
||||||
|
|
||||||
|
if(release.version === 'NEXT' && !config.next) return {
|
||||||
|
body: '',
|
||||||
|
links,
|
||||||
|
};
|
||||||
|
|
||||||
|
let body = "## " + release.version;
|
||||||
|
|
||||||
|
if( release.date) {
|
||||||
|
body += " " + release.date;
|
||||||
|
}
|
||||||
|
|
||||||
|
body += "\n\n";
|
||||||
|
|
||||||
|
const type_map = Object.fromEntries(source.change_types.flatMap(
|
||||||
|
entry => entry.keywords.map( k => [ k, entry ])
|
||||||
|
));
|
||||||
|
|
||||||
|
const changes = release.changes.map( change => typeof change === 'string'? { desc: change, type: '' } : change );
|
||||||
|
|
||||||
|
const unknown = R.uniq( changes.map(R.prop('type')).map(
|
||||||
|
t => t ?? ''
|
||||||
|
).filter( type => !type_map[type]));
|
||||||
|
|
||||||
|
if( unknown.length ) {
|
||||||
|
throw new Error(
|
||||||
|
"unknown change types encountered: " + unknown.map(t => `'${t}'`).join(', ')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( const change_type of source.change_types ) {
|
||||||
|
const type_changes = changes.filter(
|
||||||
|
({type}) => change_type.keywords.includes(type)
|
||||||
|
);
|
||||||
|
|
||||||
|
if( type_changes.length === 0) continue;
|
||||||
|
|
||||||
|
if( change_type.title )
|
||||||
|
body += mkd.h2( change_type.title) + "\n\n";
|
||||||
|
|
||||||
|
for( const c of type_changes ) {
|
||||||
|
const res = render_change(config,source)(c);
|
||||||
|
body += mkd.li(res.body,1)+"\n";
|
||||||
|
links = { ...links, ...res.links};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { body, links };
|
||||||
|
};
|
||||||
|
|
||||||
|
const handler = async (config) => {
|
||||||
|
const source = await fs.readFile(config.source,'utf-8').then(yaml.parse);
|
||||||
|
|
||||||
|
let output = '';
|
||||||
|
|
||||||
|
let {header,links} = render_header(source);
|
||||||
|
|
||||||
|
output += header + "\n\n";
|
||||||
|
|
||||||
|
for ( const res of source.releases.map( render_release(config,source) )) {
|
||||||
|
output += res.body + "\n\n";
|
||||||
|
links = {...links,...res.links};
|
||||||
|
}
|
||||||
|
|
||||||
|
output += "\n\n\n\n" + render_links(links);
|
||||||
|
|
||||||
|
config.consola.raw(output);
|
||||||
|
|
||||||
|
|
||||||
const handler = (...args) => {
|
|
||||||
console.log('hi!',args);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
command: 'print',
|
command: 'print',
|
||||||
desc : 'render the changelog',
|
desc : 'render the changelog',
|
||||||
builder: (yargs) => {
|
builder: (yargs) => {
|
||||||
yargs.boolean('json')
|
yargs
|
||||||
|
//.boolean('json')
|
||||||
.boolean('next')
|
.boolean('next')
|
||||||
.default('json',false)
|
// .default('json',false)
|
||||||
.default('next',true);
|
.default('next',true);
|
||||||
},
|
},
|
||||||
handler,
|
handler,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
22
src/command/print.test.js
Normal file
22
src/command/print.test.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { test, expect, vi } from 'vitest';
|
||||||
|
import { render_release } from './print.js';
|
||||||
|
import { base_changelog } from './init.js';
|
||||||
|
|
||||||
|
test( 'render_release a string', () => {
|
||||||
|
const {body} = render_release({})("as is");
|
||||||
|
expect(body).toEqual('as is');
|
||||||
|
});
|
||||||
|
|
||||||
|
test( 'render_release', () => {
|
||||||
|
const consola = { error: vi.fn() };
|
||||||
|
const res= render_release({consola},base_changelog)({
|
||||||
|
version: '1.2.3',
|
||||||
|
date: 'today',
|
||||||
|
changes: [
|
||||||
|
"this",
|
||||||
|
"that",
|
||||||
|
]
|
||||||
|
});
|
||||||
|
expect(res.body).toContain('# 1.2.3 today');
|
||||||
|
expect(res.body).toContain('* this');
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user