Skip to content

Commit

Permalink
feat(vite): enable watch mode for preview (nrwl#14412)
Browse files Browse the repository at this point in the history
  • Loading branch information
vicb authored and Jack Hsu committed Jan 18, 2023
1 parent 5adfc09 commit 7c76c76
Show file tree
Hide file tree
Showing 14 changed files with 123 additions and 76 deletions.
3 changes: 2 additions & 1 deletion docs/generated/packages/react/generators/library.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@
"unitTestRunner": {
"type": "string",
"enum": ["jest", "vitest", "none"],
"description": "Test runner to use for unit tests."
"description": "Test runner to use for unit tests.",
"x-prompt": "What unit test runner should be used?"
},
"inSourceTests": {
"type": "boolean",
Expand Down
5 changes: 5 additions & 0 deletions docs/generated/packages/vite/executors/build.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@
"force": {
"description": "Force the optimizer to ignore the cache and re-bundle",
"type": "boolean"
},
"watch": {
"description": "Enable re-building when files change.",
"type": "object",
"default": null
}
},
"definitions": {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ describe(componentTestGenerator.name, () => {
> = assertMinimumCypressVersion as never;
beforeEach(() => {
tree = createTreeWithEmptyV1Workspace();
jest
.spyOn(enquirer, 'prompt')
.mockReturnValue(new Promise((res) => res({ runner: 'jest' })));
});
it('should create component test for tsx files', async () => {
mockedAssertMinimumCypressVersion.mockReturnValue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ describe('React:CypressComponentTestConfiguration', () => {
> = assertMinimumCypressVersion as never;
beforeEach(() => {
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
jest
.spyOn(enquirer, 'prompt')
.mockReturnValue(new Promise((res) => res({ runner: 'jest' })));
});

it('should generate cypress config with vite', async () => {
Expand Down
3 changes: 0 additions & 3 deletions packages/react/src/generators/library/library.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,9 +371,6 @@ describe('lib', () => {

describe('--unit-test-runner none', () => {
it('should not generate test configuration', async () => {
jest
.spyOn(enquirer, 'prompt')
.mockReturnValue(new Promise((res) => res({ runner: 'none' })));
await libraryGenerator(tree, {
...defaultSchema,
unitTestRunner: 'none',
Expand Down
15 changes: 0 additions & 15 deletions packages/react/src/generators/library/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,11 @@ import { createFiles } from './lib/create-files';
import { updateBaseTsConfig } from './lib/update-base-tsconfig';
import { extractTsConfigBase } from '../../utils/create-ts-config';
import { installCommonDependencies } from './lib/install-common-dependencies';
import { prompt } from 'enquirer';
import { setDefaults } from './lib/set-defaults';

export async function libraryGenerator(host: Tree, schema: Schema) {
const tasks: GeneratorCallback[] = [];

// Check if unit test runner was provided or if we have a default
if (!schema.unitTestRunner) {
schema.unitTestRunner = (
await prompt<{ runner: 'vitest' | 'jest' | 'none' }>([
{
message: 'What unit test runner should be used?',
type: 'select',
name: 'runner',
choices: ['vitest', 'jest', 'none'],
},
])
).runner;
}

const options = normalizeOptions(host, schema);
if (options.publishable === true && !schema.importPath) {
throw new Error(
Expand Down
3 changes: 2 additions & 1 deletion packages/react/src/generators/library/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@
"unitTestRunner": {
"type": "string",
"enum": ["jest", "vitest", "none"],
"description": "Test runner to use for unit tests."
"description": "Test runner to use for unit tests.",
"x-prompt": "What unit test runner should be used?"
},
"inSourceTests": {
"type": "boolean",
Expand Down
3 changes: 0 additions & 3 deletions packages/react/src/generators/stories/stories.lib.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ describe('react:stories for libraries', () => {
let appTree: Tree;

beforeEach(async () => {
jest
.spyOn(enquirer, 'prompt')
.mockReturnValue(new Promise((res) => res({ runner: 'jest' })));
appTree = await createTestUILib('test-ui-lib');

// create another component
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ describe('react:storybook-configuration', () => {
mockedInstalledCypressVersion.mockReturnValue(10);
jest.spyOn(logger, 'warn').mockImplementation(() => {});
jest.spyOn(logger, 'debug').mockImplementation(() => {});
jest
.spyOn(enquirer, 'prompt')
.mockReturnValue(new Promise((res) => res({ runner: 'jest' })));
});

afterEach(() => {
Expand Down
51 changes: 48 additions & 3 deletions packages/vite/src/executors/build/build.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { copyAssets } from '@nrwl/js';
import { existsSync } from 'fs';
import { resolve } from 'path';

export default async function viteBuildExecutor(
export default async function* viteBuildExecutor(
options: ViteBuildExecutorOptions,
context: ExecutorContext
) {
Expand All @@ -24,7 +24,7 @@ export default async function viteBuildExecutor(
}
);

await runInstance(buildConfig);
const watcherOrOutput = await runInstance(buildConfig);

const libraryPackageJson = resolve(projectRoot, 'package.json');
const rootPackageJson = resolve(context.root, 'package.json');
Expand All @@ -49,11 +49,56 @@ export default async function viteBuildExecutor(
);
}

return { success: true };
if ('on' in watcherOrOutput) {
// watcherOrOutput is a RollupWatcher.
// event is a RollupWatcherEvent.
const emitter = makeEmitter();
let success = true;
watcherOrOutput.on('event', (event: any) => {
if (event.code === 'START') {
success = true;
} else if (event.code === 'ERROR') {
success = false;
} else if (event.code === 'END') {
emitter.push({ success });
}
// result must be closed when present.
// see https://rollupjs.org/guide/en/#rollupwatch
event.result?.close();
});
yield* emitter;
} else {
yield { success: true };
}
}

function runInstance(options: InlineConfig) {
return build({
...options,
});
}

/**
* Helper to create an async iterator.
* Calling push on the returned object emits the value.
*/
function makeEmitter() {
const events = [];
let resolve: (value: unknown) => void | null;

return {
push: (event) => {
events.push(event);
resolve?.(event);
resolve = null;
},
[Symbol.asyncIterator]: () => ({
next: async () => {
if (events.length == 0) {
await new Promise((r) => (resolve = r));
}
return { value: events.shift(), done: false };
},
}),
};
}
1 change: 1 addition & 0 deletions packages/vite/src/executors/build/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export interface ViteBuildExecutorOptions {
logLevel?: 'info' | 'warn' | 'error' | 'silent';
mode?: string;
ssr?: boolean | string;
watch?: object | null;
}
5 changes: 5 additions & 0 deletions packages/vite/src/executors/build/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@
"force": {
"description": "Force the optimizer to ignore the cache and re-bundle",
"type": "boolean"
},
"watch": {
"description": "Enable re-building when files change.",
"type": "object",
"default": null
}
},
"definitions": {},
Expand Down
100 changes: 59 additions & 41 deletions packages/vite/src/executors/preview-server/preview-server.impl.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ExecutorContext, parseTargetString, runExecutor } from '@nrwl/devkit';
import { InlineConfig, mergeConfig, preview } from 'vite';
import { InlineConfig, mergeConfig, preview, PreviewServer } from 'vite';
import {
getNxTargetOptions,
getViteSharedConfig,
Expand All @@ -22,20 +22,12 @@ export default async function* vitePreviewServerExecutor(
// Merge the options from the build and preview-serve targets.
// The latter takes precedence.
const mergedOptions = {
...{ watch: {} },
...buildTargetOptions,
...options,
};

// Launch the build target.
const target = parseTargetString(options.buildTarget, context.projectGraph);
const build = await runExecutor(target, mergedOptions, context);
for await (const result of build) {
if (!result.success) {
return result;
}
}

// Launch the server.
// Retrieve the server configuration.
const serverConfig: InlineConfig = mergeConfig(
getViteSharedConfig(mergedOptions, options.clearScreen, context),
{
Expand All @@ -48,41 +40,67 @@ export default async function* vitePreviewServerExecutor(
console.warn('WARNING: preview is not meant to be run in production!');
}

try {
const server = await preview(serverConfig);
server.printUrls();
let server: PreviewServer | undefined;

const processOnExit = async () => {
const { httpServer } = server;
// closeAllConnections was added in Node v18.2.0
httpServer.closeAllConnections && httpServer.closeAllConnections();
httpServer.close(() => {
process.off('SIGINT', processOnExit);
process.off('SIGTERM', processOnExit);
process.off('exit', processOnExit);
});
};
const processOnExit = async () => {
await closeServer(server);
process.off('SIGINT', processOnExit);
process.off('SIGTERM', processOnExit);
process.off('exit', processOnExit);
};

process.on('SIGINT', processOnExit);
process.on('SIGTERM', processOnExit);
process.on('exit', processOnExit);

process.on('SIGINT', processOnExit);
process.on('SIGTERM', processOnExit);
process.on('exit', processOnExit);
// Launch the build target.
const target = parseTargetString(options.buildTarget, context.projectGraph);
const build = await runExecutor(target, mergedOptions, context);

const resolvedUrls = [
...server.resolvedUrls.local,
...server.resolvedUrls.network,
];
for await (const result of build) {
if (result.success) {
try {
if (!server) {
server = await preview(serverConfig);
}
server.printUrls();

const resolvedUrls = [
...server.resolvedUrls.local,
...server.resolvedUrls.network,
];

yield {
success: true,
baseUrl: resolvedUrls[0] ?? '',
};
} catch (e) {
console.error(e);
yield {
success: false,
baseUrl: '',
};
yield {
success: true,
baseUrl: resolvedUrls[0] ?? '',
};
} catch (e) {
console.error(e);
yield {
success: false,
baseUrl: '',
};
}
} else {
yield {
success: false,
baseUrl: '',
};
}
}

await new Promise(() => {});
}

function closeServer(server?: PreviewServer): Promise<void> {
return new Promise((resolve) => {
if (!server) {
resolve();
} else {
const { httpServer } = server;
// closeAllConnections was added in Node v18.2.0
httpServer.closeAllConnections && httpServer.closeAllConnections();
httpServer.close(() => resolve());
}
});
}
1 change: 1 addition & 0 deletions packages/vite/src/utils/options-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ export function getViteBuildOptions(
manifest: options.manifest,
ssrManifest: options.ssrManifest,
ssr: options.ssr,
watch: options.watch,
};
}

Expand Down

0 comments on commit 7c76c76

Please sign in to comment.