forgetso
forgetso

Reputation: 2494

How do I use Vite to rebuild dependencies with npm workspaces?

Similar to this question, referencing Yarn workspaces, I have the following npm workspace structure:

package.json // root
packages
    @myscope/a
        package.json
        tsconfig.json
    @myscope/b
        package.json
        tsconfig.json
    @myscope/c
        package.json
        tsconfig.json

These packages are referenced in the root package JSON as follows

{
    "workspaces": [
        "packages/*",
    ],
}

@myscope/c depends on @myscope/b, and @myscope/b depends on @myscope/a.

Each package has its own build command and its own tsconfig.json:

{
    "build": "tsc --build --verbose tsconfig.json",
}

The tsc command builds types as well as the JS, which are critical for the imports to work when developing with local packages. I could switch these commands for a vite.config.ts that builds the JS->TS but types are not emitted. I know I can use vite-plugin-dts for this purpose.

Vite Command

If I'm running @myscope/c with the following command from within the @myscope/c folder:

npx vite serve --mode=development --config vite.config.ts

Example Repo

I've created an example repository demonstrating the layout. Inside it, running @myscope/c with vite will not rebuild @myscope/b or @myscope/a when they change despite having included preserveSymlinks in my vite.config.ts.

...
    resolve: {
        preserveSymlinks: true // this is the fix from yargs question
    }
})

Questions

Upvotes: -1

Views: 1296

Answers (1)

forgetso
forgetso

Reputation: 2494

The answer for yarn workspaces, preserveSymlinks, doesn't work for npm workspaces.

I had to write a Vite plugin to watch my local npm workspace packages and then handle updates to those files.

The gist of the plugin is:


export const VitePluginWatchWorkspace = async (config: VitePluginWatchExternalOptions): Promise<Plugin<any>> => {

    // get a list of external files you want to watch
    const externalFiles = await getExternalFileLists()


    return {
        name: 'vite-plugin-watch-workspace',

        // on build start, add the external files to Vite's watch list
        async buildStart() {
            Object.keys(externalFiles).map((file) => {
                this.addWatchFile(file)
            })
        },

        // when the external files change, rebuild them with esbuild
        async handleHotUpdate({ file, server }) {
            log(`File', ${file}`)

            const tsconfigPath = externalFiles[file]
            if (!tsconfigPath) {
                log(`tsconfigPath not found for file ${file}`)
                return
            }
            const tsconfig = getTsConfigFollowExtends(tsconfigPath)
            const fileExtension = path.extname(file)
            const loader = getLoader(fileExtension)
            const outdir = getOutDir(file, tsconfig)
            const outfile = getOutFile(outdir, file, fileExtension)
            log(`Outfile ${outfile}, loader ${loader}`)
            const buildResult = await build({
                tsconfig: tsconfigPath,
                stdin: {
                    contents: fs.readFileSync(file, 'utf8'),
                    loader,
                    resolveDir: path.dirname(file),
                },
                outfile,
                platform: config.format === 'cjs' ? 'node' : 'neutral',
                format: config.format || 'esm',
            })
            log(`buildResult', ${JSON.stringify(buildResult)}`)


            // tell the server that the file has updated
            server.ws.send({
                type: 'update',
                updates: [
                    {
                        acceptedPath: file,
                        type: 'js-update',
                        path: file,
                        timestamp: Date.now(),
                    },
                ],
            })
        },
    }
}

You can download the plugin here and view the source code here.

Upvotes: 0

Related Questions