Compare commits

...

2 Commits

Author SHA1 Message Date
Yanick Champoux 0ed4ca9cab Merge branch 'lord6-print' 2023-05-10 14:37:24 -04:00
Yanick Champoux 01e5381b77 add print command
fix #6
2023-05-10 14:36:56 -04:00
8 changed files with 211 additions and 47 deletions

View File

@ -1,12 +1,16 @@
project:
name: null
homepage: null
name: changelord
homepage: https://git.babyl.ca/yanick/changelord.js
with_stats: true
ticket_url: null
releases:
- version: NEXT
changes: []
changes:
- port the Perl changelord to JavaScript.
change_types:
- title: ''
level: minor
keywords: ['']
- title: Features
level: minor
keywords:

6
Taskfile.yaml Normal file
View File

@ -0,0 +1,6 @@
# https://taskfile.dev
version: '3'
tasks:
test: vitest run src
test:dev: vitest src

View File

@ -18,10 +18,13 @@
"dependencies": {
"consola": "^3.1.0",
"fs-extra": "^11.1.1",
"markdown-utils": "^1.0.0",
"remeda": "^1.14.0",
"yaml": "^2.2.2",
"yargs": "^17.7.2"
},
"devDependencies": {
"prettier": "^2.8.8"
"prettier": "^2.8.8",
"vitest": "^0.31.0"
}
}

9
prettier.config.mjs Normal file
View File

@ -0,0 +1,9 @@
export default {
endOfLine: "lf",
semi: true,
singleQuote: false,
tabWidth: 2,
trailingComma: "es5",
bracketSpacing: true,
proseWrap: "always",
};

View File

@ -3,6 +3,8 @@
import { hideBin } from 'yargs/helpers';
import yargs from 'yargs';
import { join } from 'path';
import yaml from 'yaml';
import fs from 'fs-extra';
import print from './command/print.js'
import init from './command/init.js'
@ -11,9 +13,10 @@ import consola from 'consola';
consola.raw = (...args) => console.log(...args);
yargs(hideBin(process.argv))
.config({
consola
consola,
})
.default('source', join( process.cwd(), 'CHANGELOG.yml' ))
.describe('source', 'changelog source')

View File

@ -1,50 +1,52 @@
import fs from 'fs-extra';
import { consola } from 'consola';
import { stringify } from 'yaml';
import fs from "fs-extra";
import { consola } from "consola";
import { stringify } from "yaml";
const change_types = [
{ title: 'Features' , level: 'minor', keywords: [ 'feat' ] } ,
{ title : 'Bug fixes' , level : 'patch', keywords : [ 'fix' ] },
{ title : 'Package maintenance' , level : 'patch', keywords : [ 'chore', 'maint', 'refactor' ] },
{ title : 'Statistics' , level : 'patch', keywords : [ 'stats' ] },
];
{ title: "", level: "minor", keywords: [""] },
{ title: "Features", level: "minor", keywords: ["feat"] },
{ title: "Bug fixes", level: "patch", keywords: ["fix"] },
{
title: "Package maintenance",
level: "patch",
keywords: ["chore", "maint", "refactor"],
},
{ title: "Statistics", level: "patch", keywords: ["stats"] },
];
const base_changelog = {
project: {
name: null,
homepage: null,
with_stats: true,
ticket_url: null,
},
releases: [
{ version: 'NEXT', changes: [] }
],
change_types,
export const base_changelog = {
project: {
name: null,
homepage: null,
with_stats: true,
ticket_url: null,
},
releases: [{ version: "NEXT", changes: [] }],
change_types,
};
const handler = async (config) => {
if( await fs.pathExists(config.source) ) {
consola.error(`${config.source} already exist, aborting.`);
process.exit();
}
if (await fs.pathExists(config.source)) {
consola.error(`${config.source} already exist, aborting.`);
process.exit();
}
consola.start(`creating ${config.source}...`);
consola.start(`creating ${config.source}...`);
await fs.writeFile( config.source, stringify(base_changelog) );
consola.success('done!');
await fs.writeFile(config.source, stringify(base_changelog));
consola.success("done!");
};
export default {
command: 'init',
desc : 'initialize new changelog source file',
builder: (yargs) => {
yargs.boolean('json')
.boolean('next')
.default('json',false)
.default('next',true);
},
handler,
}
command: "init",
desc: "initialize new changelog source file",
builder: (yargs) => {
yargs
.boolean("json")
.boolean("next")
.default("json", false)
.default("next", true);
},
handler,
};

View File

@ -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 {
command: 'print',
desc : 'render the changelog',
builder: (yargs) => {
yargs.boolean('json')
yargs
//.boolean('json')
.boolean('next')
.default('json',false)
// .default('json',false)
.default('next',true);
},
handler,
}

22
src/command/print.test.js Normal file
View 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');
});