Skip to content

Commit

Permalink
feat: initial code (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
ForbesLindesay authored May 3, 2020
1 parent e2b147d commit 23180d0
Show file tree
Hide file tree
Showing 10 changed files with 415 additions and 39 deletions.
11 changes: 10 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ workflows:
<<: *not_master
name: node-12
version: '12'
- unit-tests:
<<: *not_master
name: node-14
version: '14'

release:
jobs:
Expand All @@ -33,6 +37,10 @@ workflows:
<<: *only_master
name: node-12
version: '12'
- unit-tests:
<<: *only_master
name: node-14
version: '14'

- publish-dry-run:
<<: *only_master
Expand All @@ -50,6 +58,7 @@ workflows:
requires:
- node-10
- node-12
- node-14
- publish-approval

jobs:
Expand Down Expand Up @@ -90,7 +99,7 @@ commands:
command: node -v && npm -v && yarn -v
- run:
name: Install Dependencies
command: yarn install --pure-lockfile
command: yarn install --frozen-lockfile

build:
steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/FUNDING.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ github: [ForbesLindesay]
patreon: ForbesLindesay
open_collective: # Replace with an open collevite username
ko_fi: # Replace with a single Ko-fi username
tidelift: # npm/@forbeslindesay/npm-package-template
tidelift: # npm/parameter-reducers
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
Expand Down
105 changes: 76 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,40 +1,87 @@
# npm-package-template

A template for npm packages built in TypeScript

## Setting Up the New Repo

1. Hit "Use This Template" to create the repository
1. Enable [CircleCI](https://circleci.com/add-projects/gh/ForbesLindesay)
1. Enable [Change Log Version](https://changelogversion.com) using [My Change Log Version Installation](https://github.com/settings/installations/7328191)
1. In Settings
1. Disable "Wikis"
1. Disable "Projects"
1. Disable "Allow merge commits"
1. Disable "Allow rebase merging"
1. Enable "Automatically delete head branches"
1. Create a new branch
1. Commit initial code to the branch (be sure to replace all refernces to npm-package-template, and remove these instructions from the README)
1. Push the new branch and create a PR
1. In Settings -> Branch Protection, create a new rule
1. Use "master" as the branch name pattern
1. Enable "Require status checks to pass before merging"
1. Select the unit tests and changelog as required
1. Enable "Include administrators"
1. Enable "Restrict who can push to matching branches"
1. Merge the PR
# parameter-reducers

Use reducers to build type safe CLI parameter parsers.

## Installation

```
yarn add @forbeslindesay/npm-package-template
yarn add parameter-reducers
```

## Usage

```ts
import add from '@forbeslindesay/npm-package-template';
import {parse, startChain, param} from 'parameter-reducers';

const globalParams = startChain()
.addParam(param.flag(['-h', '--help'], 'help'))
.addParam(
param.parsedString(['-l', '--logLevel'], 'logLevel', (str, key) => {
switch (str) {
case 'debug':
case 'info':
case 'warn':
case 'error':
return {valid: true, value: str};
default:
return {
valid: false,
reason: `${key} should be one of debug, finfo, warn or error`,
};
}
}),
);

const params = startChain()
.addParam(globalParams)
.addParam(param.string(['-n', '--name'], 'name'))
.addParam(param.flag(['-v', '--verified'], 'verified'))
.addParam(param.flag(['-f', '--force'], 'force'));

test('parse empty array', () => {
const result = parse(params, []);

expect(result).toEqual({
valid: true,
rest: [],
parsed: {},
});
});

test('parse some valid args then some not valid args', () => {
const result = parse(params, [
'-h',
'--logLevel',
'info',
'--verified',
'oops',
'--name',
'Forbes Lindesay',
]);

expect(result).toEqual({
valid: true,
rest: ['oops', '--name', 'Forbes Lindesay'],
parsed: {
help: true,
logLevel: 'info',
verified: true,
},
});
});

test('parse duplicate key', () => {
const result = parse(params, [
'-h',
'--logLevel',
'info',
'--logLevel',
'warn',
]);

const result = add(2, 3);
// => 5
expect(result).toEqual({
valid: false,
reason: 'You have specified more than one value for --logLevel',
});
});
```
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"name": "@forbeslindesay/npm-package-template",
"description": "npm-package-template",
"name": "parameter-reducers",
"description": "Use reducers to build type safe CLI parameter parsers",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"repository": "git@github.com:ForbesLindesay/npm-package-template.git",
"repository": "git@github.com:ForbesLindesay/parameter-reducers.git",
"author": "Forbes Lindesay <forbes@lindesay.co.uk>",
"license": "MIT",
"scripts": {
Expand All @@ -26,6 +26,7 @@
"rimraf": "^3.0.2",
"ts-jest": "^25.3.1",
"tslint": "^6.1.1",
"type-assertions": "^1.1.0",
"typescript": "^3.8.3"
},
"jest": {
Expand Down
96 changes: 93 additions & 3 deletions src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,95 @@
import add from '../';
import * as ta from 'type-assertions';
import {parse, startChain, param} from '..';

test('add', () => {
expect(add(1, 2)).toBe(3);
const globalParams = startChain()
.addParam(param.flag(['-h', '--help'], 'help'))
.addParam(
param.parsedString(['-l', '--logLevel'], 'logLevel', (str, key) => {
switch (str) {
case 'debug':
case 'info':
case 'warn':
case 'error':
return {valid: true, value: str};
default:
return {
valid: false,
reason: `${key} should be one of debug, finfo, warn or error`,
};
}
}),
);

const params = startChain()
.addParam(globalParams)
.addParam(param.string(['-n', '--name'], 'name'))
.addParam(param.flag(['-v', '--verified'], 'verified'))
.addParam(param.flag(['-f', '--force'], 'force'));

test('parse empty array', () => {
const result = parse(params, []);

ta.assert<
ta.Equal<
typeof result,
| {
valid: false;
reason: string;
}
| {
valid: boolean;
rest: string[];
parsed: Partial<{
help: boolean;
logLevel: 'debug' | 'info' | 'warn' | 'error';
name: string;
verified: boolean;
force: boolean;
}>;
}
>
>();

expect(result).toEqual({
valid: true,
rest: [],
parsed: {},
});
});

test('parse some valid args then some not valid args', () => {
const result = parse(params, [
'-h',
'--logLevel',
'info',
'--verified',
'oops',
'--name',
'Forbes Lindesay',
]);

expect(result).toEqual({
valid: true,
rest: ['oops', '--name', 'Forbes Lindesay'],
parsed: {
help: true,
logLevel: 'info',
verified: true,
},
});
});

test('parse duplicate key', () => {
const result = parse(params, [
'-h',
'--logLevel',
'info',
'--logLevel',
'warn',
]);

expect(result).toEqual({
valid: false,
reason: 'You have specified more than one value for --logLevel',
});
});
20 changes: 20 additions & 0 deletions src/chain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {ParameterReducer} from './types';

export type Chain<T> = ParameterReducer<T> & {
addParam: <S>(param: ParameterReducer<S>) => Chain<T & S>;
};

export function startChain<T>(
param: ParameterReducer<T> = () => undefined,
): Chain<T> {
return Object.assign(
<S>(input: string[], parsed: S) => param(input, parsed),
{
addParam: <S>(childParam: ParameterReducer<S>): Chain<T & S> =>
startChain(
(input, parsed) =>
childParam(input, parsed) || (param(input, parsed) as any),
),
},
);
}
41 changes: 39 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,40 @@
export default function add(a: number, b: number) {
return a + b;
import * as param from './parameters';
import {ParameterReducer} from './types';

export {param};
export {ParameterReducerResult, ParameterReducer} from './types';
export {Chain, startChain} from './chain';

export function parse<T>(
parameters: ParameterReducer<T>,
input: string[],
):
| {
valid: false;
reason: string;
}
| {
valid: boolean;
rest: string[];
parsed: Partial<T>;
} {
let rest = input;
let parsed = {};
while (rest.length) {
const result = parameters(rest, parsed);
if (result === undefined) {
break;
}
if (result.valid) {
rest = result.rest;
parsed = result.parsed;
} else {
return result;
}
}
return {
valid: true,
rest,
parsed,
};
}
Loading

0 comments on commit 23180d0

Please sign in to comment.