diff --git a/packages/wxt/e2e/tests/encoding.test.ts b/packages/wxt/e2e/tests/encoding.test.ts new file mode 100644 index 00000000..0364c9ff --- /dev/null +++ b/packages/wxt/e2e/tests/encoding.test.ts @@ -0,0 +1,25 @@ +import { readFile } from 'node:fs/promises'; +import { describe, expect, it } from 'vitest'; +import { TestProject } from '../utils'; + +describe('Encoding', () => { + it('should convert unicode characters to ascii escaped chars', async () => { + // See more details about this test, see: + // https://github.com/wxt-dev/wxt/issues/353#issuecomment-4093271292 + const KNOWN_BAD_CHAR = '￿'; + const ESCAPED_BAD_CHAR = '\\uFFFF'; + + const project = new TestProject(); + + // `project.addFile` writes the file as UTF8 + project.addFile( + 'entrypoints/example.ts', + `export default defineUnlistedScript(() => console.log('${KNOWN_BAD_CHAR}'))`, + ); + await project.build(); + + const file = project.resolvePath('.output/chrome-mv3/example.js'); + const asciiOutput = await readFile(file, 'ascii'); + expect(asciiOutput).toContain(ESCAPED_BAD_CHAR); + }); +}); diff --git a/packages/wxt/src/core/builders/vite/index.ts b/packages/wxt/src/core/builders/vite/index.ts index 7853191a..298bf4df 100644 --- a/packages/wxt/src/core/builders/vite/index.ts +++ b/packages/wxt/src/core/builders/vite/index.ts @@ -1,4 +1,10 @@ +import { Hookable } from 'hookable'; +import { mkdir, readdir, rename, rmdir, stat } from 'node:fs/promises'; +import { dirname, extname, join, relative } from 'node:path'; import type * as vite from 'vite'; +import { ViteNodeRunner } from 'vite-node/client'; +import { ViteNodeServer } from 'vite-node/server'; +import { installSourcemapsSupport } from 'vite-node/source-map'; import { BuildStepOutput, Entrypoint, @@ -9,25 +15,19 @@ import { WxtDevServer, WxtHooks, } from '../../../types'; -import * as wxtPlugins from './plugins'; +import { normalizePath } from '../../utils'; +import { toArray } from '../../utils/arrays'; import { getEntrypointBundlePath, isHtmlEntrypoint, } from '../../utils/entrypoints'; +import { createExtensionEnvironment } from '../../utils/environments'; +import { safeVarName } from '../../utils/strings'; import { VirtualEntrypointType, VirtualModuleId, } from '../../utils/virtual-modules'; -import { Hookable } from 'hookable'; -import { toArray } from '../../utils/arrays'; -import { safeVarName } from '../../utils/strings'; -import { ViteNodeServer } from 'vite-node/server'; -import { ViteNodeRunner } from 'vite-node/client'; -import { installSourcemapsSupport } from 'vite-node/source-map'; -import { createExtensionEnvironment } from '../../utils/environments'; -import { dirname, extname, join, relative } from 'node:path'; -import { mkdir, readdir, rename, rmdir, stat } from 'node:fs/promises'; -import { normalizePath } from '../../utils'; +import * as wxtPlugins from './plugins'; export async function createViteBuilder( wxtConfig: ResolvedConfig, @@ -75,6 +75,10 @@ export async function createViteBuilder( // @ts-ignore: Untyped option: config.legacy.skipWebSocketTokenCheck = true; + // Solves https://github.com/wxt-dev/wxt/issues/353 + config.esbuild ??= {}; + if (config.esbuild) config.esbuild.charset = 'ascii'; + const server = getWxtDevServer?.(); config.plugins ??= [];