diff --git a/.changeset/rspack-import-meta.md b/.changeset/rspack-import-meta.md new file mode 100644 index 00000000..3b766543 --- /dev/null +++ b/.changeset/rspack-import-meta.md @@ -0,0 +1,5 @@ +--- +'@tanstack/devtools': patch +--- + +Fix Rspack compatibility by avoiding direct `import.meta` access patterns and add a regression test to prevent reintroduction. diff --git a/packages/devtools/src/components/source-inspector.tsx b/packages/devtools/src/components/source-inspector.tsx index 6ae5401e..519bcb51 100644 --- a/packages/devtools/src/components/source-inspector.tsx +++ b/packages/devtools/src/components/source-inspector.tsx @@ -80,7 +80,7 @@ export const SourceInspector = () => { e.stopPropagation() // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - const baseUrl = new URL(import.meta?.env?.BASE_URL ?? '/', location.origin) + const baseUrl = new URL(import.meta.env?.BASE_URL ?? '/', location.origin) const url = new URL( `__tsd/open-source?source=${encodeURIComponent( highlightState.dataSource, diff --git a/packages/devtools/src/core.tsx b/packages/devtools/src/core.tsx index ec338dcc..f282c286 100644 --- a/packages/devtools/src/core.tsx +++ b/packages/devtools/src/core.tsx @@ -65,7 +65,7 @@ export class TanStackDevtoolsCore { // tsup-preset-solid statically replaces this variable during build, which eliminates this code from server bundle // can be run outside of vite so we ignore the rule // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - if (import.meta?.env?.SSR) return + if (import.meta.env?.SSR) return if (this.#isMounted) { throw new Error('Devtools is already mounted') diff --git a/packages/devtools/tests/import-meta-compat.test.ts b/packages/devtools/tests/import-meta-compat.test.ts new file mode 100644 index 00000000..8092add7 --- /dev/null +++ b/packages/devtools/tests/import-meta-compat.test.ts @@ -0,0 +1,50 @@ +import { readdirSync, readFileSync } from 'node:fs' +import { extname, join } from 'node:path' +import { describe, it } from 'vitest' + +const sourceRoot = join(process.cwd(), 'src') +const sourceExtensions = new Set(['.ts', '.tsx']) +const forbiddenPatterns = [ + /import\.meta\?\./, + /typeof\s+import\.meta(?!\.)/, + /=\s*import\.meta(?!\.)/, +] + +const getSourceFiles = (dir: string): Array => { + const entries = readdirSync(dir, { withFileTypes: true }) + return entries.flatMap((entry) => { + const path = join(dir, entry.name) + if (entry.isDirectory()) { + return getSourceFiles(path) + } + if (sourceExtensions.has(extname(path))) { + return [path] + } + return [] + }) +} + +describe('import.meta compatibility', () => { + it('avoids direct import.meta access that breaks Rspack parsing', () => { + const violations = getSourceFiles(sourceRoot).flatMap((path) => { + const content = readFileSync(path, 'utf8') + const lines = content.split('\n') + return lines.flatMap((line, index) => { + const hasViolation = forbiddenPatterns.some((pattern) => + pattern.test(line), + ) + return hasViolation ? [`${path}:${index + 1}`] : [] + }) + }) + + if (violations.length > 0) { + throw new Error( + [ + 'Found direct `import.meta` usage in devtools source.', + 'Rspack only supports property access on `import.meta`.', + ...violations, + ].join('\n'), + ) + } + }) +})