diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6e2b0d..57a3148 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: - name: Setup node uses: actions/setup-node@v3 with: - node-version: 18.x + node-version: 20.x - name: Update npm run: npm i --prefer-online --no-fund --no-audit -g npm@latest - name: Install dependencies @@ -46,7 +46,7 @@ jobs: - name: Setup node uses: actions/setup-node@v3 with: - node-version: 18.x + node-version: 20.x - name: Update npm run: npm i --prefer-online --no-fund --no-audit -g npm@latest - name: Install dependencies diff --git a/.gitignore b/.gitignore index 0ab192a..c9d91dc 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ !/.gitignore !/.npmrc !/.prettierrc.json +!/.taprc !/Dockerfile !/bin/ !/CHANGELOG* diff --git a/.taprc b/.taprc new file mode 100644 index 0000000..4fbafbf --- /dev/null +++ b/.taprc @@ -0,0 +1,11 @@ +# vim: set filetype=yaml : +disable-coverage: false +allow-empty-coverage: true +allow-incomplete-coverage: true +plugin: + - "@tapjs/sinon" + - "!@tapjs/intercept" + - "@tapjs/tsx" + - "!@tapjs/typescript" +color: true +reporter: base diff --git a/README.md b/README.md index 420e145..50dd3eb 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,16 @@ ## Configuration & Usage +**Important Note** on '@tsconfig/node18/tsconfig.json' not found, and Tap: + +There is currently an [issue](https://github.com/tapjs/tapjs/issues/976) in ts-node that causes a loading problem when combined with Tap.js. There is a [recommended](https://github.com/tapjs/tapjs/issues/976#issuecomment-1824784507) workaround for this. Once you update your skeleton, swap out these plugins: + +``` +npx tap plugin add @tapjs/tsx +npx tap plugin rm @tapjs/typescript +``` + + ### CI In your projects `package.json` you can set custom CI variables to extend the default workflow. The following configuration will add a Postgres service to the test job, as well as inject the `DOTENV_KEY` environment variables for dotenv vault usage. diff --git a/lib/content/ci.yml b/lib/content/ci.yml index b2237dd..19b699f 100644 --- a/lib/content/ci.yml +++ b/lib/content/ci.yml @@ -24,7 +24,7 @@ jobs: - name: Setup node uses: actions/setup-node@v3 with: - node-version: 18.x + node-version: 20.x - name: Update npm run: npm i --prefer-online --no-fund --no-audit -g npm@latest - name: Install dependencies @@ -62,7 +62,7 @@ jobs: - name: Setup node uses: actions/setup-node@v3 with: - node-version: 18.x + node-version: 20.x - name: Update npm run: npm i --prefer-online --no-fund --no-audit -g npm@latest - name: Install dependencies diff --git a/lib/content/clean.ts b/lib/content/clean.ts index 9613424..f9c08ba 100755 --- a/lib/content/clean.ts +++ b/lib/content/clean.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env ts-node +#!/usr/bin/env tsx // This file is managed by code-skeleton. Do not make changes. import { rmSync } from "node:fs"; diff --git a/lib/content/gitignore b/lib/content/gitignore index 0ab192a..c9d91dc 100644 --- a/lib/content/gitignore +++ b/lib/content/gitignore @@ -10,6 +10,7 @@ !/.gitignore !/.npmrc !/.prettierrc.json +!/.taprc !/Dockerfile !/bin/ !/CHANGELOG* diff --git a/lib/content/update-shebang.ts b/lib/content/update-shebang.ts index 445dd77..9bfe153 100755 --- a/lib/content/update-shebang.ts +++ b/lib/content/update-shebang.ts @@ -1,11 +1,11 @@ -#!/usr/bin/env ts-node +#!/usr/bin/env tsx import { spawnSync } from "node:child_process"; import { readFile, writeFile } from "node:fs/promises"; import { dirname, resolve } from "node:path"; const ROOT = dirname(__dirname); -const tsShebang = "#!/usr/bin/env ts-node"; +const tsShebang = "#!/usr/bin/env tsx"; const jsShebang = "#!/usr/bin/env node"; async function updateShebang (path: string) { diff --git a/lib/index.ts b/lib/index.ts index 424c716..49d4057 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -66,28 +66,35 @@ export default async function (root: string, variables: Variables) { prepack: "tsc --project tsconfig.build.json", } ), - }, - tap: { - coverage: true, - ts: true, + postinstall: "tap build", }, types: "lib/index.d.ts", devDependencies: { - "@tsconfig/node18": "^18.0.0", - "@types/node": "^18.0.0", - "@types/tap": "^15.0.0", + "@tsconfig/node20": "^20.0.0", + "@types/node": "^20.0.0", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", "eslint": "^8.0.0", - "tap": "^16.0.0", - "ts-node": "^10.0.0", + "tap": "^18.7.0", + "tsx": "4.2.1", "typescript": "^5.0.0" }, + "overrides": { + // Needed with the 4.2.1 tsx version lock to fix code coverage + // https://github.com/privatenumber/tsx/issues/433 + "@tapjs/tsx": { + "tsx": "$tsx" + } + }, + removeDependencies: [ + "@types/tap", + "@tsconfig/node18" + ] }), "tsconfig.json": json({ set: { "//": "This file is partially managed by code-skeleton. Changes may be overwritten.", - extends: "@tsconfig/node18/tsconfig.json", + extends: "@tsconfig/node20/tsconfig.json", }, append: { include: [ diff --git a/package.json b/package.json index 3d6b3b9..3b502b6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@code4rena/skeleton", - "version": "1.2.1", + "version": "2.0.0-2", "description": "", "main": "lib/index.js", "scripts": { @@ -13,7 +13,8 @@ "postlint": "npm run skeleton:verify", "skeleton:apply": "code-skeleton apply", "preskeleton:verify": "npm run prepack", - "skeleton:verify": "code-skeleton verify" + "skeleton:verify": "code-skeleton verify", + "postinstall": "tap build" }, "keywords": [], "author": "Nathan LaFreniere ", @@ -22,20 +23,27 @@ "mustache": "^4.2.0" }, "devDependencies": { - "@tsconfig/node18": "^18.0.0", + "@tapjs/sinon": "^1.1.18", + "@tapjs/tsx": "^1.1.19", + "@tsconfig/node20": "^20.0.0", "@types/mustache": "^4.0.0", - "@types/node": "^18.0.0", - "@types/tap": "^15.0.0", + "@types/node": "^20.0.0", + "@types/sinon": "^17.0.3", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", "eslint": "^8.0.0", - "tap": "^16.0.0", - "ts-node": "^10.0.0", - "typescript": "5.2.2" + "tap": "^18.7.0", + "tsx": "4.2.1", + "typescript": "^5.0.0" }, "peerDependencies": { "code-skeleton": "^2.0.0" }, + "overrides": { + "@tapjs/tsx": { + "tsx": "$tsx" + } + }, "skeleton": { "module": ".", "variables": { @@ -54,9 +62,5 @@ "lib/**/*.d.ts", "!lib/types/**", "lib/content/**" - ], - "tap": { - "coverage": true, - "ts": true - } + ] } diff --git a/scripts/clean.ts b/scripts/clean.ts index 9613424..f9c08ba 100755 --- a/scripts/clean.ts +++ b/scripts/clean.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env ts-node +#!/usr/bin/env tsx // This file is managed by code-skeleton. Do not make changes. import { rmSync } from "node:fs"; diff --git a/scripts/update-shebang.ts b/scripts/update-shebang.ts index 445dd77..9bfe153 100755 --- a/scripts/update-shebang.ts +++ b/scripts/update-shebang.ts @@ -1,11 +1,11 @@ -#!/usr/bin/env ts-node +#!/usr/bin/env tsx import { spawnSync } from "node:child_process"; import { readFile, writeFile } from "node:fs/promises"; import { dirname, resolve } from "node:path"; const ROOT = dirname(__dirname); -const tsShebang = "#!/usr/bin/env ts-node"; +const tsShebang = "#!/usr/bin/env tsx"; const jsShebang = "#!/usr/bin/env node"; async function updateShebang (path: string) { diff --git a/test/index.test.ts b/test/index.test.ts deleted file mode 100644 index 7b8fbc8..0000000 --- a/test/index.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import t from "tap"; - -void t.test("this is not a real test", (t) => { - t.pass(); - t.end(); -}); \ No newline at end of file diff --git a/test/mustache.test.ts b/test/mustache.test.ts new file mode 100644 index 0000000..87d4b1e --- /dev/null +++ b/test/mustache.test.ts @@ -0,0 +1,91 @@ +import t from "tap"; +import { mustache } from "../lib/mustache"; + +void t.test("mustache", async (t) => { + await t.test("must include a path", (t) => { + // @ts-expect-error bad data on purpose + t.throws(() => mustache({})); + t.end(); + }); + + await t.test("should generate", async (t) => { + const { mustache } = t.mockRequire("../lib/mustache", { + "node:fs/promises": { + readFile: (path: string) => { + t.equal(path, "path/to/file.txt"); + return Promise.resolve("<% title %>"); + } + } + }); + const variables = { + title: "it generates" + }; + + const generator = mustache({ + path: "path/to/file.txt", + variables, + }); + + const output = await generator.generate(); + t.equal(output, "it generates"); + }); + + await t.test("should validate partials", async (t) => { + const { mustache } = t.mockRequire("../lib/mustache", { + "node:fs/promises": { + readFile: () => Promise.resolve("<% title %>") + } + }); + const variables = { + title: "it generates" + }; + + const generator = mustache({ + path: "path/to/file.txt", + variables, + }); + + const generateSpy = t.sinon.spy(generator, "generate"); + const reportSpy = t.sinon.spy(generator, "report"); + + await generator.validate({ + path: "path/to/file.txt", + found: "it generates\nand handles extras" + }); + + t.equal(generateSpy.callCount, 1); + // Report not called because we passed + t.equal(reportSpy.callCount, 0); + }); + + await t.test("should fail validation when base template not found", async (t) => { + const { mustache } = t.mockRequire("../lib/mustache", { + "node:fs/promises": { + readFile: () => Promise.resolve("<% title %>") + } + }); + const variables = { + title: "it generates" + }; + + const generator = mustache({ + path: "path/to/file.txt", + variables, + }); + + const generateSpy = t.sinon.spy(generator, "generate"); + const reportSpy = t.sinon.spy(generator, "report"); + + await generator.validate({ + path: "path/to/file.txt", + found: "a new file" + }); + + t.equal(generateSpy.callCount, 1); + t.ok(reportSpy.calledOnceWithExactly({ + expected: "it generates", + found: "a new file", + message: "path/to/file.txt does not include the original template" + })); + }); +}); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 646e651..78bbd7e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "@tsconfig/node18/tsconfig.json", + "extends": "@tsconfig/node20/tsconfig.json", "include": [ "**/*.ts", ".eslintrc.js"