Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,7 @@ jobs:
pull-requests: write
uses: fastify/workflows/.github/workflows/plugins-ci.yml@ef591e2186785d5ab36b9fe6a79c7ce2f1d94e57 # v7.0.0
with:
node-versions: '["22", "24", "26"]'
license-check: true
lint: true

37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,44 @@ npm i @fastify/flash

Flash messages are stored in the session. First, we need to register the session plugin: [@fastify/secure-session](https://www.npmjs.com/package/@fastify/secure-session).

### ESM

```javascript
import { readFileSync } from 'node:fs'
import { join } from 'node:path'
import Fastify from 'fastify'
import fastifySession from '@fastify/secure-session'
import fastifyFlash from '@fastify/flash'

const fastify = Fastify()

fastify.register(fastifySession, {
// adapt this to point to the directory where secret-key is located
key: readFileSync(join(import.meta.dirname, 'secret-key')),
cookie: {
// options from setCookie, see https://github.com/fastify/fastify-cookie
path: "/"
}
})
fastify.register(fastifyFlash, {
prefix: "/"
})

fastify.get('/test', (req, reply) => {
req.flash('warning', ['username required', 'password required'])

const warning = reply.flash('warning')
reply.send({ warning }) // {"warning":["username required","password required"]}
})
```

### CommonJS

Even though this package ships as ESM, it can still be `require()`'d from a CommonJS project (see the [Install](#install) note above):

```javascript
const fs = require('node:fs')
const path = require('node:path')
const fastify = require('fastify')()
const fastifySession = require('@fastify/secure-session')
const fastifyFlash = require('@fastify/flash')
Expand Down
File renamed without changes.
16 changes: 13 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@
"name": "@fastify/flash",
"version": "6.0.5",
"description": "Flash message plugin for fastify.",
"main": "./lib",
"type": "commonjs",
"type": "module",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"exports": {
".": {
"types": "./lib/index.d.ts",
"default": "./lib/index.js"
}
},

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to confirm, you mean main/types are redundant now that exports is present, right? 😅

-  "main": "./lib/index.js",
   "type": "module",
-  "types": "./lib/index.d.ts",
   "exports": {
     ".": {
       "types": "./lib/index.d.ts",
       "default": "./lib/index.js"
     }
   },

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why using exports when you only have single entrypoint?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why using exports when you only have single entrypoint?

The only advantage, now that I think about it is: "encapsulation".
The exports field explicitly defines the package's public interface. Without it, nothing prevents a user from running require('@fastify/flash/lib/flash.js') and depending on an internal file. With exports, that import throws ERR_PACKAGE_PATH_NOT_EXPORTED, so we're free to restructure the internals of lib/ later without it being a breaking change.

That said, we could also just drop it and keep this instead:

   "main": "./lib/index.js",
   "type": "module",
   "types": "./lib/index.d.ts",
-  "exports": {
-    ".": {
-      "types": "./lib/index.d.ts",
-      "default": "./lib/index.js"
-    }
-  },

Happy to go either way, let me know which you'd prefer 😉

"scripts": {
"build": "npm run clean-build && npm run lint && tsc -p ./tsconfig.build.json",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this building correctly for release? is tsconfig.build.json correct?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, verified locally on Node 24.18.0. Commands to reproduce:

npm run build
ls -la lib/
node -e "require('./lib/index.js'); console.log('require(esm) works')"

Output:

  • lib/index.js, lib/index.d.ts, lib/flash.js, lib/flash.d.ts produced
  • lib/index.js matches exports["."].default
  • lib/index.d.ts matches exports["."].types
  • require('./lib/index.js') resolves and logs "require(esm) works" confirms the exports map works for CJS consumers too

tsconfig.build.json is correct: its include: ["src"] overrides(not merges with) the base config's broader include, so test/ is correctly excluded and only the paths declared in exports get produced.


test

"clean-build": "rimraf ./lib && mkdir lib",
Expand Down Expand Up @@ -57,7 +64,7 @@
"devDependencies": {
"@fastify/secure-session": "^8.0.0",
"@types/node": "^26.0.0",
"borp": "^0.21.0",
"borp": "^1.0.0",
"eslint": "^9.35.0",
"fastify": "^5.0.0",
"neostandard": "^0.13.0",
Expand All @@ -72,5 +79,8 @@
],
"publishConfig": {
"access": "public"
},
"engines": {
"node": ">=20.19.0 <21 || >=22.12.0"
}
}
7 changes: 3 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import fp = require('fastify-plugin')
import { flashFactory } from './flash'
import fp from 'fastify-plugin'
import { flashFactory } from './flash.ts'

declare module 'fastify' {
export interface FastifyRequest {
Expand All @@ -10,10 +10,9 @@ declare module 'fastify' {
}
}

export = fp<{ core?: string }>(
export default fp<{ core?: string }>(
function (fastify, _opts, done) {
const flash = flashFactory()

fastify.decorateRequest('flash', flash.request)
fastify.decorateReply('flash', flash.reply)
done()
Expand Down
8 changes: 4 additions & 4 deletions test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { test, TestContext } from 'node:test'
import { test, type TestContext } from 'node:test'
import { join } from 'node:path'
import { readFileSync } from 'node:fs'
import Fastify from 'fastify'
import fastifySession, { Session, SessionData } from '@fastify/secure-session'
import fastifySession, { type Session, type SessionData } from '@fastify/secure-session'
import querystring from 'node:querystring'
import fastifyFlash from '../src'
import fastifyFlash from '../src/index.ts'

const key = readFileSync(join(__dirname, '..', '..', 'secret-key'))
const key = readFileSync(join(import.meta.dirname, '..', 'secret-key'))

test('should set error message and and clear up after displaying.', async (t: TestContext) => {
t.plan(5)
Expand Down
10 changes: 7 additions & 3 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
{
"compilerOptions": {
"moduleResolution": "node16",
"target": "esnext",
"module": "nodenext",
"moduleResolution": "nodenext",
"allowImportingTsExtensions": true,
"rewriteRelativeImportExtensions": true,
"verbatimModuleSyntax": true,
"erasableSyntaxOnly": true,
"declaration": true,
"target": "es2022",
"module": "node16",
"outDir": "lib",
"pretty": true,
"noEmitOnError": true,
Expand Down
Loading