Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: tolerate syntax errors #1437

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 30 additions & 3 deletions packages/wxt/src/core/create-server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { debounce } from 'perfect-debounce';
import chokidar from 'chokidar';
import {
BuildStepOutput,
EntrypointGroup,
Expand Down Expand Up @@ -28,6 +29,7 @@ import {
mapWxtOptionsToRegisteredContentScript,
} from './utils/content-scripts';
import { createKeyboardShortcuts } from './keyboard-shortcuts';
import { isBabelSyntaxError, logBabelSyntaxError } from './utils/syntax-errors';

/**
* Creates a dev server and pre-builds all the files that need to exist before loading the extension.
Expand Down Expand Up @@ -156,8 +158,25 @@ async function createServerInternal(): Promise<WxtDevServer> {
const keyboardShortcuts = createKeyboardShortcuts(server);

const buildAndOpenBrowser = async () => {
// Build after starting the dev server so it can be used to transform HTML files
server.currentOutput = await internalBuild();
try {
// Build after starting the dev server so it can be used to transform HTML files
server.currentOutput = await internalBuild();
} catch (err) {
if (!isBabelSyntaxError(err)) {
throw err;
}
logBabelSyntaxError(err);
wxt.logger.info('Waiting for syntax error to be fixed...');
await new Promise<void>((resolve) => {
const watcher = chokidar.watch(err.id, { ignoreInitial: true });
watcher.on('all', () => {
watcher.close();
wxt.logger.info('Syntax error resolved, rebuilding...');
resolve();
});
});
return buildAndOpenBrowser();
}

// Add file watchers for files not loaded by the dev server. See
// https://github.com/wxt-dev/wxt/issues/428#issuecomment-1944731870
Expand Down Expand Up @@ -187,7 +206,7 @@ function createFileReloader(server: WxtDevServer) {
const cb = async (event: string, path: string) => {
changeQueue.push([event, path]);

await fileChangedMutex.runExclusive(async () => {
const reloading = fileChangedMutex.runExclusive(async () => {
if (server.currentOutput == null) return;

const fileChanges = changeQueue
Expand Down Expand Up @@ -256,6 +275,14 @@ function createFileReloader(server: WxtDevServer) {
// Catch build errors instead of crashing. Don't log error either, builder should have already logged it
}
});

await reloading.catch((error) => {
if (!isBabelSyntaxError(error)) {
throw error;
}
// Log syntax errors without crashing the server.
logBabelSyntaxError(error);
});
};

return debounce(cb, wxt.config.dev.server!.watchDebounce, {
Expand Down
32 changes: 32 additions & 0 deletions packages/wxt/src/core/utils/syntax-errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { relative } from 'node:path';
import pc from 'picocolors';
import { wxt } from '../wxt';

export interface BabelSyntaxError extends SyntaxError {
code: 'BABEL_PARSER_SYNTAX_ERROR';
frame?: string;
id: string;
loc: { line: number; column: number };
}

export function isBabelSyntaxError(error: unknown): error is BabelSyntaxError {
return (
error instanceof SyntaxError &&
(error as any).code === 'BABEL_PARSER_SYNTAX_ERROR'
);
}

export function logBabelSyntaxError(error: BabelSyntaxError) {
let filename = relative(wxt.config.root, error.id);
if (filename.startsWith('..')) {
filename = error.id;
}
let message = error.message.replace(
/\(\d+:\d+\)$/,
`(${filename}:${error.loc.line}:${error.loc.column + 1})`,
);
if (error.frame) {
message += '\n\n' + pc.red(error.frame);
}
wxt.logger.error(message);
}