Skip to content

Commit

Permalink
feat(vite): add support for incremental builds on serve
Browse files Browse the repository at this point in the history
  • Loading branch information
Coly010 committed May 22, 2024
1 parent 503cf8e commit 8c46e46
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 0 deletions.
73 changes: 73 additions & 0 deletions e2e/vite/src/vite-crystal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import {
runCLI,
runCommandUntil,
uniq,
updateFile,
} from '@nx/e2e/utils';
import { ChildProcess } from 'child_process';
import { names } from '@nx/devkit';

const myApp = uniq('my-app');
const myVueApp = uniq('my-vue-app');
Expand Down Expand Up @@ -63,6 +65,77 @@ describe('@nx/vite/plugin', () => {
}, 200_000);
});

describe('should support buildable libraries', () => {
it('should build the library and application successfully', () => {
const myApp = uniq('myapp');
runCLI(
`generate @nx/react:app ${myApp} --bundler=vite --unitTestRunner=vitest`
);

const myBuildableLib = uniq('mybuildablelib');
runCLI(
`generate @nx/react:library ${myBuildableLib} --bundler=vite --unitTestRunner=vitest --buildable`
);

const exportedLibraryComponent = names(myBuildableLib).className;

updateFile(
`${myApp}/src/app/App.tsx`,
`import NxWelcome from './nx-welcome';
import { ${exportedLibraryComponent} } from '@proj/${myBuildableLib}';
export function App() {
return (
<div>
<${exportedLibraryComponent} />
<NxWelcome title="viteib" />
</div>
);
}
export default App;`
);

updateFile(
`${myApp}/vite.config.ts`,
`/// <reference types='vitest' />
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
export default defineConfig({
root: __dirname,
cacheDir: '../../node_modules/.vite/${myApp}',
server: {
port: 4200,
host: 'localhost',
},
preview: {
port: 4300,
host: 'localhost',
},
plugins: [react(), nxViteTsPaths({buildLibsFromSource: false})],
build: {
outDir: '../../dist/${myApp}',
emptyOutDir: true,
reportCompressedSize: true,
commonjsOptions: {
transformMixedEsModules: true,
},
},
});`
);

const result = runCLI(`build ${myApp}`);
expect(result).toContain('1/1 dependent project tasks succeeded');
expect(result).toContain(
`Successfully ran target build for project ${myApp}`
);
});
});

it('should run serve-static', async () => {
let process: ChildProcess;
const port = 8081;
Expand Down
10 changes: 10 additions & 0 deletions packages/vite/plugins/nx-tsconfig-paths.plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
createTmpTsConfig,
} from '@nx/js/src/utils/buildable-libs-utils';
import { Plugin } from 'vite';
import { nxViteBuildCoordinationPlugin } from './nx-vite-build-coordination.plugin';

export interface nxViteTsPathsOptions {
/**
Expand Down Expand Up @@ -107,6 +108,15 @@ There should at least be a tsconfig.base.json or tsconfig.json in the root of th
relative(workspaceRoot, projectRoot),
dependencies
);

if (config.command === 'serve') {
const buildableLibraryDependencies = dependencies
.filter((dep) => dep.node.type === 'lib')
.map((dep) => dep.node.name)
.join(',');
const buildCommand = `npx nx run-many --target=${process.env.NX_TASK_TARGET_TARGET} --projects=${buildableLibraryDependencies}`;
config.plugins.push(nxViteBuildCoordinationPlugin({ buildCommand }));
}
}

const parsed = loadConfig(foundTsConfigPath);
Expand Down
71 changes: 71 additions & 0 deletions packages/vite/plugins/nx-vite-build-coordination.plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { type Plugin } from 'vite';
import { BatchFunctionRunner } from 'nx/src/command-line/watch/watch';
import { exec, type ChildProcess } from 'child_process';
import {
daemonClient,
type UnregisterCallback,
} from 'nx/src/daemon/client/client';
import { output } from 'nx/src/utils/output';

export interface NxViteBuildCoordinationPluginOptions {
buildCommand: string;
}
export function nxViteBuildCoordinationPlugin(
options: NxViteBuildCoordinationPluginOptions
): Plugin {
let activeBuildProcess: ChildProcess | undefined;
let unregisterFileWatcher: UnregisterCallback | undefined;

async function buildChangedProjects() {
await new Promise<void>((res) => {
activeBuildProcess = exec(options.buildCommand);
activeBuildProcess.stdout.pipe(process.stdout);
activeBuildProcess.stderr.pipe(process.stderr);
activeBuildProcess.on('exit', () => {
res();
});
activeBuildProcess.on('error', () => {
res();
});
});
activeBuildProcess = undefined;
}

function createFileWatcher() {
const runner = new BatchFunctionRunner(() => buildChangedProjects());
return daemonClient.registerFileWatcher(
{ watchProjects: 'all' },
(err, { changedProjects, changedFiles }) => {
if (err === 'closed') {
output.error({
title: 'Watch connection closed',
bodyLines: [
'The daemon had closed the connection to this watch process.',
'Please restart your watch command.',
],
});
process.exit(1);
}

if (activeBuildProcess) {
activeBuildProcess.kill(2);
activeBuildProcess = undefined;
}

runner.enqueue(changedProjects, changedFiles);
}
);
}

return {
name: 'nx-vite-build-coordination-plugin',
async buildStart() {
if (!unregisterFileWatcher) {
await buildChangedProjects();
unregisterFileWatcher = await createFileWatcher();
process.on('exit', () => unregisterFileWatcher());
process.on('SIGINT', () => process.exit());
}
},
};
}

0 comments on commit 8c46e46

Please sign in to comment.