ngrok-module
ngrok-module copied to clipboard
Nuxt 3 Support
I've taken the liberty of using this module as a basis and creating a Nuxt 3 compatible module:
import ngrok from "ngrok";
import type { Ngrok } from "ngrok";
import chalk from "chalk";
import { defineNuxtModule } from "@nuxt/kit";
import type { Server as HttpServer } from "http";
import type { Server as HttpsServer } from "https";
import consola from "consola";
// https://github.com/bubenshchykov/ngrok
export type ModuleOptions = Partial<Ngrok.Options>;
declare module "@nuxt/schema" {
interface NuxtConfig {
[CONFIG_KEY]?: ModuleOptions;
}
}
const CONFIG_KEY = "ngrok";
export default defineNuxtModule({
meta: {
// Usually npm package name of your module
name: "ngrok",
// The key in `nuxt.config` that holds your module options
configKey: CONFIG_KEY,
// Compatibility constraints
compatibility: {
nuxt: "^3.0.0",
},
},
// Default configuration options for your module
defaults: {
authtoken: process.env.NGROK_TOKEN,
auth: process.env.NGROK_AUTH,
},
hooks: {},
async setup(moduleOptions, nuxt) {
// Don't start NGROK in production mode
if (nuxt.options.dev === false) {
return;
}
if (!moduleOptions.auth) {
// eslint-disable-next-line no-console
consola.warn(
"[ngrok] Dev server exposed to internet without password protection! Consider using `ngrok.auth` options"
);
}
// Start NGROK when Nuxt server is listening
let url: string;
nuxt.hook(
"listen",
async (_server: HttpServer | HttpsServer, listener: any) => {
if (moduleOptions.authtoken) {
await ngrok.authtoken(moduleOptions.authtoken);
}
const { port } = new URL(listener.url);
url = await ngrok.connect({
...moduleOptions,
addr: port,
} as Ngrok.Options);
nuxt.options.publicRuntimeConfig.ngrok = { url };
consola.success(chalk.underline.green(`ngrok connected: ${url}`));
}
);
// Disconnect ngrok connection when closing nuxt
nuxt.hook("close", () => {
if (url) {
ngrok.disconnect();
consola.success(chalk.underline.yellow("ngrok disconnected"));
url = null;
}
});
},
});
There is a bug in Nuxt 3 where if we modify the nuxt config file, the listen hook is not called again after the close hook.
Additionally, right now I seem to have to use this config:
vite: {
server: {
hmr: {
protocol: "ws",
host: "127.0.0.1",
},
}
}
This prevents an infinite reload when accessing via the ngrok domain..
This is a WIP and no sure how to resolve the vite hmr issue in a more robust way right now. Happy for someone else to take over or if there's a solution I'm happy to implement and create a PR.
I have a working module for Nuxt 3 if interested:
import ngrok from "ngrok";
import type { Ngrok } from "ngrok";
import chalk from "chalk";
import { defineNuxtModule, addTemplate } from "@nuxt/kit";
import consola from "consola";
import { IncomingMessage } from "connect";
import { ServerResponse } from "http";
// https://github.com/bubenshchykov/ngrok
export type ModuleOptions = Partial<Ngrok.Options>;
const CONFIG_KEY = "ngrok";
export default defineNuxtModule({
meta: {
// Usually npm package name of your module
name: "ngrok",
// The key in `nuxt.config` that holds your module options
configKey: CONFIG_KEY,
// Compatibility constraints
compatibility: {
nuxt: "^3.0.0",
},
},
// Default configuration options for your module
defaults: {
authtoken: process.env.NGROK_TOKEN,
auth: process.env.NGROK_AUTH,
},
hooks: {},
setup: async (moduleOptions: ModuleOptions, nuxt) => {
const CREATE_NGROK_TEMPLATE = (url) => {
addTemplate({
filename: 'ngrok.mjs',
write: true,
getContents: () => `export const url = ${JSON.stringify(url)}`,
});
}
// Don't start NGROK in production mode
if (nuxt.options.dev === false) {
CREATE_NGROK_TEMPLATE(null)
return;
}
if (!moduleOptions.auth) {
// eslint-disable-next-line no-console
consola.warn(
"[ngrok] Dev server exposed to internet without password protection! Consider using `ngrok.auth` options"
);
}
let url: string;
nuxt.hook("listen", async (_server: any, { port }: { port: number }) => {
if (moduleOptions.authToken) {
await ngrok.authtoken(moduleOptions.authToken);
}
url = await ngrok.connect({
...moduleOptions,
addr: port,
} as Ngrok.Options);
CREATE_NGROK_TEMPLATE(url)
consola.success(chalk.underline.green(`ngrok connected: ${url}`));
});
// Disconnect ngrok connection when closing nuxt
nuxt.hook("close", () => {
if (url) {
ngrok.disconnect();
consola.success(chalk.underline.yellow("ngrok disconnected"));
url = null;
}
});
},
});
Instead of populating the public runtime config it creates a build file in .nuxt/ngrok.mjs
You can import this elsewhere
import { url as ngrokUrl } from '#build/ngrok.mjs'
That's all :)
I now have an issue, I was able to solve the web socket infinite reloading because connection failed before using this in nuxt config
server: {
hmr: {
protocol: "ws",
host: "127.0.0.1",
},
}
But this workaround no longer works over http or https.. is anyone able to offer any clues on how to make this work?