Tunnelling a Production Build

Hi Shopify Team and Enthusiasts,

As we are going forward and sharing different QA, testers, and engineering teams we needed a way to iterate fast and show the latest changes.
As the current --tunnel option in the dev command only ships the development server we found out it takes over 2.5 minutes to load the maps, scripts, full libraries and HMR, and to iterate fast it is not suitable, nor does it qualify as good tests, as development scripts run slower than compiled builds.

Therefore we created a workaround that can be implemented also publicly if it meets Shopify’s criteria (we would have opened a PR but the repo is not public).

The workaround is simple, if --tunnel option is used we consider it as a production build, then we route it to the web-server.js file to instead of opening a development server, building the package, and serving a preview of the build instead, thus minimizing the wait time to just a few seconds, and tunnelling a production worthy build, for testers and other QA teams.

2 Easy Steps:

  1. Go to node_modules/@shopify/shop-minis-cli/build/commands/dev/utils/web-server.js
    Paste this instead of the original:
import { createServer, build, preview } from 'vite';
import { outputDebug } from '@shopify/cli-kit/node/output';
import { DEV_SERVER_PORT, DEV_SERVER_HOST } from '../../../constants/dev-server.js';
import { createViteConfig } from '../../../utils/vite-config.js';

export { createViteConfig } from '../../../utils/vite-config.js';

export const createWebServer = async ({ prod = false } = {}) => {
    const baseConfig = createViteConfig();
 
    if (prod) {
        console.log("Running a production build with preview")
        // 1) Build the app once
        await build(baseConfig);

        // 2) Serve the built files (dist) via Vite preview
        const previewServer = await preview({
            ...baseConfig,
            preview: {
                host: DEV_SERVER_HOST,
                port: DEV_SERVER_PORT,
                strictPort: true,
                allowedHosts: [
                    '.ngrok.app',
                    '.ngrok-free.app'
                ],
            },
        });

        outputDebug(
            `Preview (prod) server is running on http://${DEV_SERVER_HOST}:${DEV_SERVER_PORT}`,
        );
        return previewServer;
    }

    // default: dev server with HMR
    const vite = await createServer({
        ...baseConfig,
        server: {
            host: DEV_SERVER_HOST,
            port: DEV_SERVER_PORT,
            strictPort: true,
            /* @ts-ignore-next-line */
            allowedHosts: [
                '.ngrok.app',
                '.ngrok-free.app'
            ],
        },
    });

    await vite.listen(DEV_SERVER_PORT);
    outputDebug(`Dev server is running on http://${DEV_SERVER_HOST}:${DEV_SERVER_PORT}`);
    return vite;
};

The changes are:

  • adding “prod” parameter to the function
  • if the prod parameter is true, first build the app, then preview it.
  • if not, continue as usual.
  1. Go to node_modules/@shopify/shop-minis-cli/build/commands/dev/index.js
    Inside the loadCommand function, in the try/catch of the command add a variable to check if the tunnel option is used this way:
            const options = initialOptions;
            const useProdServer = Boolean(options.tunnel); //This is the addition
            if (options.tunnel && !process.env.NGROK_AUTHTOKEN) {
                throw new AbortError('Error starting development server', 'To use the --tunnel flag, you must have an ngrok authtoken set in the NGROK_AUTHTOKEN environment variable. Get your authtoken at https://dashboard.ngrok.com/get-started/setup');
            }

And find the createWebServer command, two or three lines below, and send our newly created variable inside:

await createWebServer({ prod: useProdServer });

That is it, you can now tunnel your production build, scanning the QR or sharing the URL from the dev panel works.

Note for all, we used a paid plan with ngrok, and have not tested it with the free plan.

Note for Shopify Engineering, this was used to make our dev cycle smoother, and does not intend to infringe any TOS. If it does however not meet any criteria or infringe anything I will delete the post without any question.

hi @ofek,

In general, we do not recommend using this method. It may interfere with submitting, updating, or other Minis CLI commands. Altering node_modules could cause your mini to run in situations where it should not, and if that happens, we will reject the submission.

Adding a test build to distribute among testers or stakeholders is on our roadmap, and we will implement it soon. We know it is a highly requested feature. :slight_smile:
I am glad this patch works for you in the meantime.

Well noted, it is indeed a temporary workaround, and we will make sure to reverse the changes once submitting so to not interfere.