Skip to content

Commit

Permalink
Merge remote-tracking branch 'ts-package-template/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
seb-cr committed Dec 5, 2023
2 parents 2cc23f6 + 9266e53 commit d91ab49
Show file tree
Hide file tree
Showing 13 changed files with 1,181 additions and 887 deletions.
1,934 changes: 1,089 additions & 845 deletions package-lock.json

Large diffs are not rendered by default.

31 changes: 17 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"description": "A template for TypeScript CLI projects",
"author": "Seb Aebischer",
"license": "MIT",
"main": "dist/index.js",
"type": "module",
"exports": "./dist/index.js",
"repository": {
"type": "git",
"url": "git+https://github.com/seb-cr/ts-cli-template.git"
Expand All @@ -23,34 +24,36 @@
"build": "tsc -p tsconfig-build.json",
"clean": "rm -rf dist",
"lint": "eslint src tests",
"mocha": "mocha --require ts-node/register --require tests/setup.ts",
"mocha": "mocha --node-option loader=@istanbuljs/esm-loader-hook --node-option loader=./tests/loader.js --require tests/setup.ts",
"test": "npm run mocha -- 'tests/**/*.spec.ts'",
"coverage": "nyc npm test"
},
"devDependencies": {
"@comicrelief/eslint-config": "^2.0.3",
"@istanbuljs/esm-loader-hook": "^0.2.0",
"@istanbuljs/nyc-config-typescript": "^1.0.2",
"@octokit/rest": "^19.0.13",
"@types/chai": "^4.3.5",
"@types/inquirer": "^8.2.6",
"@types/mocha": "^10.0.1",
"@types/node": "^18.17.1",
"@octokit/rest": "^20.0.2",
"@types/chai": "^4.3.11",
"@types/inquirer": "^9.0.7",
"@types/mocha": "^10.0.6",
"@types/node": "^18.19.0",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"chai": "^4.3.7",
"chalk": "^4.1.2",
"eslint": "^8.46.0",
"eslint-plugin-import": "^2.28.0",
"chai": "^4.3.10",
"chalk": "^5.3.0",
"eslint": "^8.54.0",
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-jsdoc": "^39.9.1",
"inquirer": "^8.2.6",
"inquirer": "^9.2.12",
"mocha": "^10.2.0",
"nyc": "^15.1.0",
"stdout-stderr": "^0.1.13",
"ts-node": "^10.9.1",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.1.6"
"typescript": "^5.3.2"
},
"dependencies": {
"commander": "^10.0.1"
"commander": "^10.0.1",
"es-main": "^1.3.0"
}
}
4 changes: 2 additions & 2 deletions scripts/set-up-semantic-release.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { readFileSync, writeFileSync } from 'fs';
import { readFileSync, writeFileSync } from 'node:fs';

import { Octokit } from '@octokit/rest';

Expand All @@ -7,7 +7,7 @@ import {
sh,
step,
warn,
} from './setup';
} from './setup.js';

const SEMANTIC_RELEASE_CONFIG = (branch: string) => `# Semantic Release config
# See https://semantic-release.gitbook.io/semantic-release/usage/configuration
Expand Down
17 changes: 9 additions & 8 deletions scripts/setup.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
#!/usr/bin/env npx ts-node
import { ExecOptions, exec } from 'child_process';
import { readFileSync, rmSync, writeFileSync } from 'fs';
import { basename, dirname } from 'path';
import { ExecOptions, exec } from 'node:child_process';
import { readFileSync, rmSync, writeFileSync } from 'node:fs';
import { basename, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

import chalk from 'chalk';
import esMain from 'es-main';
import inquirer from 'inquirer';

import { setUpSemanticRelease } from './set-up-semantic-release';
import { setUpSemanticRelease } from './set-up-semantic-release.js';

export type Answers = {
packageName: string;
Expand Down Expand Up @@ -53,11 +55,10 @@ export async function step(name: string, action: () => void | Promise<void>) {
}

export async function setup(initialAnswers?: Partial<Answers>) {
const rootDir = dirname(__dirname);
const rootDir = dirname(dirname(fileURLToPath(import.meta.url)));
process.chdir(rootDir);

// eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
const packageJson = require('../package.json');
const packageJson = JSON.parse(readFileSync('package.json').toString());
const gitUsername = await sh('git config user.name');
const gitRemote = await sh('git remote get-url origin');
const gitBranch = await sh('git rev-parse --abbrev-ref HEAD');
Expand Down Expand Up @@ -194,7 +195,7 @@ export async function setup(initialAnswers?: Partial<Answers>) {
}

/* istanbul ignore if */
if (module === require.main) {
if (esMain(import.meta)) {
setup().catch((error) => {
console.error(error);
process.exitCode = 1;
Expand Down
9 changes: 5 additions & 4 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#!/usr/bin/env node
import { Command, CommanderError } from 'commander';
import esMain from 'es-main';

import { greet } from '..';
import { greet } from '../index.js';
import pkg from '../package.js';

type CliOptions = {
name: string;
Expand All @@ -13,8 +15,7 @@ type CliOptions = {
* @param args
*/
function getOptions(args: string[]): CliOptions {
// eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
const { version, description, bin } = require('../../package.json');
const { version, description, bin } = pkg;
const name = Object.keys(bin)[0];

const cmd = new Command()
Expand Down Expand Up @@ -60,6 +61,6 @@ export async function cli(args: string[]): Promise<void> {

// this can't be covered by tests -- they call `cli` directly
/* istanbul ignore next */
if (module === require.main) {
if (esMain(import.meta)) {
cli(process.argv.slice(2));
}
8 changes: 8 additions & 0 deletions src/package.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { readFile } from 'node:fs/promises';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';

const modulePath = fileURLToPath(import.meta.url);
const packagePath = resolve(dirname(modulePath), '..', 'package.json');
const packageJson = await readFile(packagePath);
export default JSON.parse(packageJson.toString());
5 changes: 5 additions & 0 deletions tests/.eslintrc.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
extends:
- '@comicrelief/eslint-config/mixins/mocha'

overrides:
- files: loader.js
rules:
jsdoc/require-jsdoc: off
2 changes: 1 addition & 1 deletion tests/cli/cli.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect } from 'chai';

import { invoke } from './invoke';
import { invoke } from './invoke.js';

describe('cli', () => {
context('without a name specified', () => {
Expand Down
2 changes: 1 addition & 1 deletion tests/cli/invoke.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { stderr, stdout } from 'stdout-stderr';

import { cli } from '@/src/cli';
import { cli } from '@/src/cli/index.js';

export interface InvokeResult {
exitCode: number;
Expand Down
2 changes: 1 addition & 1 deletion tests/greet.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect } from 'chai';

import { greet } from '@/src';
import { greet } from '@/src/index.js';

describe('greet', () => {
context('with a name specified', () => {
Expand Down
32 changes: 32 additions & 0 deletions tests/loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
Custom ESM loader to get TypeScript `paths` config working with `ts-node`.
Based on https://github.com/TypeStrong/ts-node/discussions/1450
`ts-node` may support this natively if they eventually merge
https://github.com/TypeStrong/ts-node/pull/1585
*/

import { pathToFileURL } from 'url';

import { resolve as resolveTs } from 'ts-node/esm';
import * as tsConfigPaths from 'tsconfig-paths';

const { absoluteBaseUrl, paths } = tsConfigPaths.loadConfig();
const matchPath = tsConfigPaths.createMatchPath(absoluteBaseUrl, paths);

export function resolve(specifier, ctx, defaultResolve) {
// remove `.js` extension before matching and add back on afterwards
const match = specifier.endsWith('.js')
? matchPath(specifier.slice(0, -3))?.concat('.js')
: matchPath(specifier);

return match
? resolveTs(pathToFileURL(`${match}`).href, ctx, defaultResolve)
: resolveTs(specifier, ctx, defaultResolve);
}

export {
load,
transformSource,
} from 'ts-node/esm';
9 changes: 5 additions & 4 deletions tests/setup.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { existsSync, readFileSync } from 'fs';
import { basename, dirname } from 'path';
import { existsSync, readFileSync } from 'node:fs';
import { basename, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

import { expect } from 'chai';
import chalk from 'chalk';
import inquirer, { Question } from 'inquirer';

import { Answers, setup, sh } from '../scripts/setup';
import { Answers, setup, sh } from '../scripts/setup.js';

/*
eslint-disable
Expand Down Expand Up @@ -178,7 +179,7 @@ describe('setup script', () => {
});

it('should default package name to the directory name', () => {
const dir = basename(dirname(__dirname));
const dir = basename(dirname(dirname(fileURLToPath(import.meta.url))));
expect(result).to.contain(`? Package name: ${dir}\n`);
});

Expand Down
13 changes: 6 additions & 7 deletions tsconfig-base.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
"strict": true,
"target": "es2018",
"module": "commonjs",
"target": "es2022",
"module": "node16",
"moduleResolution": "node16",
"esModuleInterop": true,
"lib": ["es2018"],
"lib": ["es2022"],
"sourceMap": true,
"declaration": true,
"declarationMap": true,
Expand All @@ -22,8 +23,6 @@
"forceConsistentCasingInFileNames": true,
},
"ts-node": {
"require": [
"tsconfig-paths/register",
],
},
"esm": true
}
}

0 comments on commit d91ab49

Please sign in to comment.