diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..43c97e7 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/.tmplr.yml b/.tmplr.yml index f54270e..36f15d0 100644 --- a/.tmplr.yml +++ b/.tmplr.yml @@ -8,6 +8,8 @@ steps: - read: project_name prompt: 'Enter project name:' + default: + from: filesystem.rootdir - read: project_description prompt: 'Enter project description:' @@ -30,7 +32,7 @@ steps: eval: '{{ install_type | matches: dev }}' steps: - read: install_node - eval: 'node install --save-dev {{ project_name }}' + eval: 'npm install --save-dev {{ project_name }}' - read: install_yarn eval: 'yarn add --dev {{ project_name }}' @@ -38,7 +40,7 @@ steps: eval: '{{ install_type | matches: global }}' steps: - read: install_node - eval: 'node install --global {{ project_name }}' + eval: 'npm install --global {{ project_name }}' - read: install_yarn eval: 'yarn global add {{ project_name }}' @@ -46,23 +48,36 @@ steps: eval: '{{ install_type | matches: dep }}' steps: - read: install_node - eval: 'node install {{ project_name }}' + eval: 'npm install {{ project_name }}' - read: install_yarn eval: 'yarn add {{ project_name }}' # setup project + # make sure to copy "hidden"/dot files (loreanvictor/tmplr#14) + + - copy: ./template/**/* + to: ./ - - update: src/cli.ts - - update: test/test.ts + - copy: ./template/**/.**/**/* + to: ./ - - copy: package.tmplr.json - to: package.json + - copy: ./template/**/.* + to: ./ - - copy: readme.tmplr.md - to: readme.md + - copy: ./template/**/.**/**/.* + to: ./ # finish + # temporaryily specify folders to remove (loreanvictor/tmplr#15) + + - remove: ./template/**/* + - remove: ./template/**/.**/**/* + - remove: ./template/**/.* + - remove: ./template/**/.**/**/.* + + - remove: ./template/.github + - remove: ./template/src + - remove: ./template/test + - remove: ./template - remove: .tmplr.yml - - remove: package.tmplr.json - - remove: readme.tmplr.md diff --git a/license b/license.md similarity index 100% rename from license rename to license.md diff --git a/package.tmplr.json b/package.tmplr.json deleted file mode 100644 index e8f7468..0000000 --- a/package.tmplr.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - "name": "{{ tmplr.project_name }}", - "version": "0.0.0", - "description": "{{ tmplr.project_description }}", - "keywords": [ - "cli", - "cli-app", - "command line" - ], - "license": "MIT", - "repository": "tommy-mitchell/{{ tmplr.project_name | skip: @tommy-mitchell/ }}", - "author": { - "name": "Tommy Mitchell", - "url": "https://tommymitchell.io" - }, - "type": "module", - "bin": { - "{{ tmplr.command_name }}": "dist/cli.js" - }, - "files": [ - "dist" - ], - "scripts": { - "build": "tsc && chmod +x dist/cli.js", - "test": "listr xo tsd 'c8 ava'" - }, - "dependencies": { - "meow": "^11.0.0" - }, - "devDependencies": { - "@tommy-mitchell/tsconfig": "^0.1.0", - "ava": "^5.2.0", - "c8": "^7.13.0", - "execa": "^7.1.1", - "get-bin-path": "^8.0.0", - "listr-cli": "^0.2.0", - "tsd": "^0.28.0", - "tsx": "^3.12.5", - "typescript": "~5.0.2", - "xo": "^0.53.1" - }, - "engines": { - "node": ">=14.8.0" - }, - "ava": { - "files": [ - "test/*.ts" - ], - "extensions": { - "ts": "module" - }, - "nodeArguments": [ - "--loader=tsx" - ] - }, - "xo": { - "rules": { - "@typescript-eslint/quotes": [ - "error", - "double" - ], - "object-shorthand": [ - "error", - "always", - { - "avoidExplicitReturnArrows": false - } - ], - "@typescript-eslint/keyword-spacing": [ - "error", - { - "overrides": { - "if": { - "after": false - }, - "for": { - "after": false - }, - "while": { - "after": false - } - } - } - ], - "arrow-parens": "off" - } - } -} diff --git a/readme.md b/readme.md index c2a87a7..5c7e56f 100644 --- a/readme.md +++ b/readme.md @@ -9,4 +9,4 @@ npx tmplr tommy-mitchell/cli-template yarn install ``` -Run inside of desired installation directory. +Run inside of desired installation directory, then add `test/tsconfig.json` to git exclude. Requires `tmplr` v0.2.4 or higher. diff --git a/template/.github/workflows/main.yml b/template/.github/workflows/main.yml new file mode 100644 index 0000000..86f4abc --- /dev/null +++ b/template/.github/workflows/main.yml @@ -0,0 +1,19 @@ +name: CI +on: + - push + - pull_request +jobs: + test: + name: Node.js ${{ matrix.node-version }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node-version: [16, 18, 20] + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - run: npm run ci diff --git a/.gitignore b/template/.gitignore similarity index 83% rename from .gitignore rename to template/.gitignore index 35ce849..02860b1 100644 --- a/.gitignore +++ b/template/.gitignore @@ -1,4 +1,5 @@ node_modules +coverage dist package-lock.json yarn.lock diff --git a/template/.xo-config.json b/template/.xo-config.json new file mode 100644 index 0000000..19b43e4 --- /dev/null +++ b/template/.xo-config.json @@ -0,0 +1,17 @@ +{ + "rules": { + "arrow-parens": "off", + "quotes": "off", + "@typescript-eslint/quotes": ["error", "double"], + "object-curly-spacing": "off", + "@typescript-eslint/object-curly-spacing": ["error", "always"], + "@typescript-eslint/no-confusing-void-expression": ["error", { "ignoreArrowShorthand": true }], + "object-shorthand": ["error", "always", { "avoidExplicitReturnArrows": false }], + "@typescript-eslint/keyword-spacing": ["error", { "overrides": { + "if": { "after": false }, + "for": {"after": false }, + "while": {"after": false }, + "catch": {"after": false } + }}] + } +} diff --git a/template/package.json b/template/package.json new file mode 100644 index 0000000..f25fa30 --- /dev/null +++ b/template/package.json @@ -0,0 +1,61 @@ +{ + "name": "{{ tmplr.project_name }}", + "version": "0.0.0", + "description": "{{ tmplr.project_description }}", + "keywords": [ + "cli", + "cli-app", + "command line" + ], + "license": "MIT", + "repository": "tommy-mitchell/{{ tmplr.project_name | skip: @tommy-mitchell/ }}", + "author": { + "name": "Tommy Mitchell", + "url": "https://tommymitchell.io" + }, + "type": "module", + "bin": { + "{{ tmplr.command_name }}": "dist/cli.js" + }, + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "npm run build", + "build": "tsc && chmodx --package", + "postbuild": "replace-in-files dist/cli.js --string='#!/usr/bin/env tsx' --replacement='#!/usr/bin/env node'", + "test": "listr xo 'c8 ava'" + }, + "dependencies": { + "meow": "^12.0.1" + }, + "devDependencies": { + "@johnowennixon/chmodx": "1.1.0", + "@tommy-mitchell/tsconfig": "^1.0.0", + "@types/node": "^16", + "ava": "^5.3.1", + "c8": "^8.0.0", + "execa": "^7.1.1", + "get-bin-path": "^10.0.0", + "is-executable": "^2.0.1", + "listr-cli": "^0.3.0", + "replace-in-files-cli": "^2.2.0", + "tsx": "^3.12.7", + "typescript": "~5.1.6", + "xo": "^0.54.2" + }, + "engines": { + "node": ">=16" + }, + "ava": { + "files": [ + "test/*.ts" + ], + "extensions": { + "ts": "module" + }, + "nodeArguments": [ + "--loader=tsx" + ] + } +} diff --git a/readme.tmplr.md b/template/readme.md similarity index 85% rename from readme.tmplr.md rename to template/readme.md index c29962c..b7b27f9 100644 --- a/readme.tmplr.md +++ b/template/readme.md @@ -16,21 +16,19 @@ ``` -*Uses top-level await. Requires Node 14.8 or higher.* - ## Usage ```sh $ {{ tmplr.command_name }} Usage - $ {{ tmplr.command_name }} + $ {{ tmplr.command_name }} […] Options Examples - + $ {{ tmplr.command_name }} ``` ## Related diff --git a/src/cli.ts b/template/src/cli.ts similarity index 53% rename from src/cli.ts rename to template/src/cli.ts index b75c66f..aea1575 100644 --- a/src/cli.ts +++ b/template/src/cli.ts @@ -1,21 +1,26 @@ #!/usr/bin/env node +import process from "node:process"; import meow from "meow"; const cli = meow(` Usage - $ {{ tmplr.command_name }} + $ {{ tmplr.command_name }} […] Options Examples - + $ {{ tmplr.command_name }} `, { importMeta: import.meta, + description: false, + help: { + type: "boolean", + shortFlag: "h", + } }); -const {input} = cli; -const {help: helpShortFlag} = cli.flags; +const { input, flags: { help: helpShortFlag } } = cli; if(input.length === 0 || helpShortFlag) { cli.showHelp(0); diff --git a/template/test/_utils.ts b/template/test/_utils.ts new file mode 100644 index 0000000..62a12e7 --- /dev/null +++ b/template/test/_utils.ts @@ -0,0 +1,7 @@ +import { fileURLToPath } from "node:url"; +import path from "node:path"; +import { type Options as ExecaOptions } from "execa"; + +export const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +export const atFixture = (name: string): ExecaOptions => ({cwd: `${__dirname}/fixtures/${name}`}); diff --git a/template/test/cli.ts b/template/test/cli.ts new file mode 100644 index 0000000..1039faa --- /dev/null +++ b/template/test/cli.ts @@ -0,0 +1,26 @@ +import anyTest, { type TestFn } from "ava"; +import { getBinPath } from "get-bin-path"; +import { isExecutable } from "is-executable"; +import {execa, type ExecaError} from "execa"; +import { __dirname, atFixture } from "./_utils.js"; + +const test = anyTest as TestFn<{ + binPath: string; + helpText: string[]; +}>; + +test.before("setup context", async t => { + const binPath = await getBinPath(); + t.truthy(binPath, "No bin path found!"); + + t.context.binPath = binPath!.replace("dist", "src").replace(".js", ".ts"); + t.true(await isExecutable(t.context.binPath), "Source binary not executable!"); + + t.context.helpText = []; +}); + +test("main", async t => { + const {exitCode} = await execa(t.context.binPath); + + t.is(exitCode, 0); +}); diff --git a/template/test/fixtures/.gitkeep b/template/test/fixtures/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/template/test/tsconfig.json b/template/test/tsconfig.json new file mode 100644 index 0000000..ca1253b --- /dev/null +++ b/template/test/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../tsconfig.test.json" +} diff --git a/template/tsconfig.json b/template/tsconfig.json new file mode 100644 index 0000000..65907cb --- /dev/null +++ b/template/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@tommy-mitchell/tsconfig", + "exclude": ["node_modules", "coverage", "dist"], + "include": ["src"], + "compilerOptions": { + "outDir": "dist", + "declaration": false + }, +} diff --git a/template/tsconfig.test.json b/template/tsconfig.test.json new file mode 100644 index 0000000..ba54f70 --- /dev/null +++ b/template/tsconfig.test.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["src"], + "include": ["test"], + "compilerOptions": { + "noEmit": true, + "noUnusedLocals": false, + }, +} diff --git a/test/test.ts b/test/test.ts deleted file mode 100644 index 3f6ec01..0000000 --- a/test/test.ts +++ /dev/null @@ -1,9 +0,0 @@ -import test from "ava"; -import {execa} from "execa"; -import {getBinPath} from "get-bin-path"; - -const binPath = await getBinPath(); - -test("main", async t => { - await execa(binPath); -}); diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 2535e5b..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "@tommy-mitchell/tsconfig", - "exclude": [ - "node_modules", - "dist", - ], - "compilerOptions": { - "outDir": "dist", - }, -}