From c0defa616ae3c5a8525e4e56b7dcec49deed2ad5 Mon Sep 17 00:00:00 2001 From: Julien Ripouteau Date: Wed, 17 Apr 2024 22:51:47 +0200 Subject: [PATCH] refactor!: resolve glob patterns from package.json directory when using `hot-hook/register` --- .changeset/healthy-houses-kneel.md | 33 +++++++++++++++++++ README.md | 2 ++ examples/fastify/package.json | 2 +- examples/hono/package.json | 2 +- examples/node_http_basic/package.json | 2 +- packages/hot_hook/src/hot.ts | 3 +- packages/hot_hook/src/loader.ts | 3 +- packages/hot_hook/src/register.ts | 8 +++-- packages/hot_hook/src/types.ts | 11 ++++++- packages/hot_hook/tests/register.spec.ts | 42 ++++++++++++++++++++++++ 10 files changed, 99 insertions(+), 9 deletions(-) create mode 100644 .changeset/healthy-houses-kneel.md diff --git a/.changeset/healthy-houses-kneel.md b/.changeset/healthy-houses-kneel.md new file mode 100644 index 0000000..8cbc76c --- /dev/null +++ b/.changeset/healthy-houses-kneel.md @@ -0,0 +1,33 @@ +--- +"hot-hook": minor +--- + +From this commit, if you are using `--import=hot-hook/register` for using hot-hook, the `package.json` will be used for resolving glob patterns in the `boundaries` config. + +That means, if you had a `package.json` with the following content, and a root entrypoint in `./src/start.ts`, glob patterns were resolved from `./src/start.ts` file : + +```json +{ + "name": "my-package", + "version": "1.0.0", + "hot-hook": { + "boundaries": [ + "./controllers/**/*", + ] + } +} +``` + +This configuration was matching all files in the `./src/controllers` directory. To achieve the same result, you should now use the following configuration: + +```json +{ + "name": "my-package", + "version": "1.0.0", + "hot-hook": { + "boundaries": [ + "./src/controllers/**/*", + ] + } +} +``` diff --git a/README.md b/README.md index 5480152..5c5a101 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,8 @@ To configure boundaries and other files, you'll need to use your application's ` } ``` +Note that glob patterns are resolved from the `package.json` directory. + Or you can still use the `import.meta.hot?.boundary` attribute in your code to specify which files should be hot reloadable. ### Using `hot.init` diff --git a/examples/fastify/package.json b/examples/fastify/package.json index 0f82f32..963b09f 100644 --- a/examples/fastify/package.json +++ b/examples/fastify/package.json @@ -19,7 +19,7 @@ }, "hotHook": { "boundaries": [ - "./services/**/*.ts" + "./src/services/**/*.ts" ] } } diff --git a/examples/hono/package.json b/examples/hono/package.json index ae39fba..3ee5df8 100644 --- a/examples/hono/package.json +++ b/examples/hono/package.json @@ -17,7 +17,7 @@ }, "hotHook": { "boundaries": [ - "./views/**/*.tsx" + "./src/views/**/*.tsx" ] } } diff --git a/examples/node_http_basic/package.json b/examples/node_http_basic/package.json index 3743147..f88180f 100644 --- a/examples/node_http_basic/package.json +++ b/examples/node_http_basic/package.json @@ -11,7 +11,7 @@ }, "hotHook": { "boundaries": [ - "./app.ts" + "./src/app.ts" ] } } diff --git a/packages/hot_hook/src/hot.ts b/packages/hot_hook/src/hot.ts index 36f2e7e..cf2b80f 100644 --- a/packages/hot_hook/src/hot.ts +++ b/packages/hot_hook/src/hot.ts @@ -58,10 +58,11 @@ class Hot { parentURL: import.meta.url, transferList: [this.#messageChannel.port2], data: { - messagePort: this.#messageChannel.port2, root: this.#options.root, ignore: this.#options.ignore, boundaries: this.#options.boundaries, + messagePort: this.#messageChannel.port2, + rootDirectory: this.#options.rootDirectory, } satisfies InitializeHookOptions, }) diff --git a/packages/hot_hook/src/loader.ts b/packages/hot_hook/src/loader.ts index 0c3829e..791860d 100644 --- a/packages/hot_hook/src/loader.ts +++ b/packages/hot_hook/src/loader.ts @@ -22,6 +22,7 @@ export class HotHookLoader { constructor(options: InitializeHookOptions) { this.#options = options this.#messagePort = options.messagePort + this.#projectRoot = options.rootDirectory! if (options.root) this.#initialize(options.root) @@ -33,8 +34,8 @@ export class HotHookLoader { * Initialize the class with the provided root path. */ #initialize(root: string) { - this.#projectRoot = dirname(root) this.#watcher = this.#createWatcher().add(root) + this.#projectRoot = this.#projectRoot ?? dirname(root) this.#pathIgnoredMatcher = new Matcher(this.#projectRoot, this.#options.ignore) this.#hardcodedBoundaryMatcher = new Matcher(this.#projectRoot, this.#options.boundaries) } diff --git a/packages/hot_hook/src/register.ts b/packages/hot_hook/src/register.ts index 59512e9..a7f4ac0 100644 --- a/packages/hot_hook/src/register.ts +++ b/packages/hot_hook/src/register.ts @@ -1,7 +1,8 @@ -import { resolve } from 'node:path' -import { hot } from './hot.js' +import { dirname, resolve } from 'node:path' import { readPackageUp } from 'read-package-up' +import { hot } from './hot.js' + const pkgJson = await readPackageUp() if (!pkgJson) { throw new Error('Could not find package.json') @@ -11,7 +12,8 @@ const { packageJson, path: packageJsonPath } = pkgJson const hotHookConfig = packageJson.hotHook await hot.init({ - root: hotHookConfig?.root ? resolve(packageJsonPath, hotHookConfig.root) : undefined, + rootDirectory: dirname(packageJsonPath), boundaries: hotHookConfig?.boundaries, ignore: ['**/node_modules/**'].concat(hotHookConfig?.ignore || []), + root: hotHookConfig?.root ? resolve(packageJsonPath, hotHookConfig.root) : undefined, }) diff --git a/packages/hot_hook/src/types.ts b/packages/hot_hook/src/types.ts index 80796d2..ba02353 100644 --- a/packages/hot_hook/src/types.ts +++ b/packages/hot_hook/src/types.ts @@ -23,6 +23,12 @@ export interface InitOptions { */ root?: string + /** + * Root Directory will be used to resolve relative paths. + * If not provided, it will be the directory of the root file. + */ + rootDirectory?: string + /** * Files that will create an HMR boundary. This is equivalent of importing * the module with `import.meta.hot.boundary` in the module. @@ -30,7 +36,10 @@ export interface InitOptions { boundaries?: string[] } -export type InitializeHookOptions = Pick & { +export type InitializeHookOptions = Pick< + InitOptions, + 'ignore' | 'root' | 'rootDirectory' | 'boundaries' +> & { /** * The message port to communicate with the parent thread. */ diff --git a/packages/hot_hook/tests/register.spec.ts b/packages/hot_hook/tests/register.spec.ts index d085e17..2a22d76 100644 --- a/packages/hot_hook/tests/register.spec.ts +++ b/packages/hot_hook/tests/register.spec.ts @@ -128,4 +128,46 @@ test.group('Register', () => { await createHandlerFile({ path: 'app.js', response: 'Hello World! Updated new' }) await supertest('http://localhost:3333').get('/').expect(200).expect('Hello World! Updated new') }) + + test('use package.json dirname as root directory', async ({ fs }) => { + await fakeInstall(fs.basePath) + + await fs.createJson('package.json', { + type: 'module', + hotHook: { boundaries: ['./src/app.js'] }, + }) + await fs.create( + 'bin/server.js', + `import * as http from 'http' + import { join } from 'node:path' + + const server = http.createServer(async (request, response) => { + const app = await import('../src/app.js') + await app.default(request, response) + }) + + server.listen(3333, () => console.log('Server is running')) + ` + ) + + await createHandlerFile({ path: 'src/app.js', response: 'Hello World!' }) + + const server = runProcess('bin/server.js', { + cwd: fs.basePath, + env: { NODE_DEBUG: 'hot-hook' }, + nodeOptions: ['--import=hot-hook/register'], + }) + + await server.waitForOutput('Server is running') + + await supertest('http://localhost:3333').get('/').expect(200).expect('Hello World!') + + await setTimeout(100) + await createHandlerFile({ path: 'src/app.js', response: 'Hello World! Updated' }) + await supertest('http://localhost:3333').get('/').expect(200).expect('Hello World! Updated') + + await setTimeout(100) + await createHandlerFile({ path: 'src/app.js', response: 'Hello World! Updated new' }) + await supertest('http://localhost:3333').get('/').expect(200).expect('Hello World! Updated new') + }) })