Compare commits

..

No commits in common. "0ed4ca9cab17dd16061182ae00103e4ddb44a851" and "7336efacbbb4cf905017ca8b96c49e8fee8432d2" have entirely different histories.

8 changed files with 47 additions and 211 deletions

View File

@ -1,16 +1,12 @@
project: project:
name: changelord name: null
homepage: https://git.babyl.ca/yanick/changelord.js homepage: null
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:

View File

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

View File

@ -18,13 +18,10 @@
"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"
} }
} }

View File

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

View File

@ -3,8 +3,6 @@
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'
@ -13,10 +11,9 @@ 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')

View File

@ -1,52 +1,50 @@
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: "", level: "minor", keywords: [""] }, { title: 'Features' , level: 'minor', keywords: [ 'feat' ] } ,
{ title: "Features", level: "minor", keywords: ["feat"] }, { title : 'Bug fixes' , level : 'patch', keywords : [ 'fix' ] },
{ title: "Bug fixes", level: "patch", keywords: ["fix"] }, { title : 'Package maintenance' , level : 'patch', keywords : [ 'chore', 'maint', 'refactor' ] },
{ { 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 = {
project: { const base_changelog = {
name: null, project: {
homepage: null, name: null,
with_stats: true, homepage: null,
ticket_url: null, with_stats: true,
}, ticket_url: null,
releases: [{ version: "NEXT", changes: [] }], },
change_types, releases: [
{ 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 yargs.boolean('json')
.boolean("json") .boolean('next')
.boolean("next") .default('json',false)
.default("json", false) .default('next',true);
.default("next", true); },
}, handler,
handler, }
};

View File

@ -1,131 +1,16 @@
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 yargs.boolean('json')
//.boolean('json')
.boolean('next') .boolean('next')
// .default('json',false) .default('json',false)
.default('next',true); .default('next',true);
}, },
handler, handler,
} }

View File

@ -1,22 +0,0 @@
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');
});