diff --git a/build/add-js-extensions.mjs b/build/add-js-extensions.mjs deleted file mode 100644 index bbaf8d99f1..0000000000 --- a/build/add-js-extensions.mjs +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env node -// Script to add .js extensions to all relative imports in TypeScript files -// This is required for ESM compatibility - -import { promises as fs } from 'fs'; -import path from 'path'; -import { fileURLToPath } from 'url'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -const rootDir = path.join(__dirname, '..'); -const srcDir = path.join(rootDir, 'src'); - -// Regex patterns to match import statements with relative paths -// Updated to handle multi-line imports/exports by using [\s\S] to match newlines -const importPatterns = [ - // import ... from './path' or '../path' (supports multi-line named imports) - /^(\s*import\s+[\s\S]+?from\s+['"])(\.\/.+?|\.\.?\/.+?)(['"])/gm, - // export ... from './path' or '../path' (supports multi-line named exports) - /^(\s*export\s+[\s\S]+?from\s+['"])(\.\/.+?|\.\.?\/.+?)(['"])/gm, - // import('./path') or import('../path') (supports newlines before parentheses and quotes) - /(\bimport[\s\S]*?\([\s\S]*?['"])(\.\/.+?|\.\.?\/.+?)(['"])/gm, - // await import('./path') (supports newlines in await import statements) - /(\bawait\s+import[\s\S]*?\([\s\S]*?['"])(\.\/.+?|\.\.?\/.+?)(['"])/gm, -]; - -async function getAllTsFiles(dir) { - const files = []; - const entries = await fs.readdir(dir, { withFileTypes: true }); - - for (const entry of entries) { - const fullPath = path.join(dir, entry.name); - - if (entry.isDirectory()) { - // Skip node_modules, out, dist, etc. - if (!['node_modules', 'out', 'dist', '.git', '.vscode', 'resources'].includes(entry.name)) { - files.push(...(await getAllTsFiles(fullPath))); - } - } else if ((entry.name.endsWith('.ts') || entry.name.endsWith('.tsx')) && !entry.name.endsWith('.d.ts')) { - // Include .ts and .tsx files, but exclude .d.ts declaration files - files.push(fullPath); - } - } - - return files; -} - -function addJsExtension(content) { - let modified = content; - let changeCount = 0; - - for (const pattern of importPatterns) { - modified = modified.replace(pattern, (match, before, importPath, after) => { - // Skip if already has an extension - if (/\.(js|ts|tsx|json|css|less|svg|png|jpg)$/i.test(importPath)) { - return match; - } - - changeCount++; - return `${before}${importPath}.js${after}`; - }); - } - - return { content: modified, changed: changeCount > 0, changeCount }; -} - -async function main() { - console.log('šŸ” Finding all TypeScript files in src/...'); - const tsFiles = await getAllTsFiles(srcDir); - console.log(`šŸ“ Found ${tsFiles.length} TypeScript files\n`); - - let totalFilesChanged = 0; - let totalImportsChanged = 0; - - for (const file of tsFiles) { - const content = await fs.readFile(file, 'utf-8'); - const { content: newContent, changed, changeCount } = addJsExtension(content); - - if (changed) { - await fs.writeFile(file, newContent, 'utf-8'); - totalFilesChanged++; - totalImportsChanged += changeCount; - const relativePath = path.relative(rootDir, file); - console.log(`āœ… ${relativePath} (${changeCount} import${changeCount > 1 ? 's' : ''})`); - } - } - - console.log(`\n✨ Done!`); - console.log(`šŸ“Š Modified ${totalFilesChanged} files`); - console.log(`šŸ”— Updated ${totalImportsChanged} import statements`); -} - -main().catch(error => { - console.error('āŒ Error:', error); - process.exit(1); -}); diff --git a/build/esbuild/build.ts b/build/esbuild/build.ts index 5761205363..06edf8765c 100644 --- a/build/esbuild/build.ts +++ b/build/esbuild/build.ts @@ -43,25 +43,27 @@ const deskTopNodeModulesToExternalize = [ // Lazy loaded modules. 'vscode-languageclient/node', '@jupyterlab/nbformat', - 'vscode-jsonrpc' + 'vscode-jsonrpc', + // vega-lite uses top-level await (through vega-canvas), must be external + 'vega-lite' ]; const commonExternals = [ 'log4js', 'vscode', 'commonjs', - 'module', // Node.js builtin module for createRequire (ESM support) - 'node:os', // Node.js builtin (use node: prefix for ESM) - 'ansi-regex', // Used by regexp utils - 'node:crypto', - 'node:fs/promises', - 'node:path', 'vscode-jsonrpc', // Used by a few modules, might as well pull this out, instead of duplicating it in separate bundles. // Ignore telemetry specific packages that are not required. 'applicationinsights-native-metrics', '@opentelemetry/tracing', '@azure/opentelemetry-instrumentation-azure-sdk', '@opentelemetry/instrumentation', - '@azure/functions-core' + '@azure/functions-core', + // Node.js builtins (with node: prefix) + 'node:crypto', + 'node:fs/promises', + 'node:os', + 'node:path', + 'ansi-regex' // Used by regexp utils ]; // Create separate copies to avoid shared-state mutations const webExternals = [...commonExternals]; @@ -237,20 +239,23 @@ function createConfig( } const isPreRelease = isDevbuild || process.env.IS_PRE_RELEASE_VERSION_OF_JUPYTER_EXTENSION === 'true'; const releaseVersionScriptFile = isPreRelease ? 'release.pre-release.js' : 'release.stable.js'; - const alias = { + const alias: Record = { moment: path.join(extensionFolder, 'build', 'webpack', 'moment.js'), 'vscode-jupyter-release-version': path.join(__dirname, releaseVersionScriptFile) }; + // Use ESM entry for jsonc-parser to avoid UMD internal require() issues when bundling if (target === 'desktop') { alias['jsonc-parser'] = path.join(extensionFolder, 'node_modules', 'jsonc-parser', 'lib', 'esm', 'main.js'); } + // Desktop builds use CommonJS for VS Code/Cursor compatibility + // Web builds use ESM for browser compatibility const config: SameShape = { entryPoints: [source], outfile, bundle: true, external, alias, - format: 'esm', + format: target === 'desktop' ? 'cjs' : 'esm', metafile: isDevbuild && !watch, define, target: target === 'desktop' ? 'node18' : 'es2018', @@ -263,18 +268,6 @@ function createConfig( loader: target === 'desktop' ? {} : loader }; - // Add createRequire banner for desktop ESM builds to support external CommonJS modules - if (target === 'desktop') { - config.banner = { - js: `import { createRequire as __createRequire } from 'module'; -import { fileURLToPath as __fileURLToPath } from 'url'; -import { dirname as __getDirname } from 'path'; -const require = __createRequire(import.meta.url); -const __filename = __fileURLToPath(import.meta.url); -const __dirname = __getDirname(__filename);` - }; - } - return config; } async function build(source: string, outfile: string, options: { watch: boolean; target: 'desktop' | 'web' }) { @@ -451,18 +444,10 @@ async function buildAll() { { target: 'desktop', watch: isWatchMode } ) ); - builders.push( - build( - path.join(extensionFolder, 'src', 'extension.node.proxy.ts'), - path.join(extensionFolder, 'dist', 'extension.node.proxy.js'), - // This file almost never ever changes, hence no need to watch this. - { target: 'desktop', watch: false } - ) - ); builders.push( ...deskTopNodeModulesToExternalize - // zeromq will be manually bundled. - .filter((module) => !['zeromq', 'zeromqold', 'vscode-jsonrpc'].includes(module)) + // zeromq will be manually bundled, vega-lite uses top-level await (can't be CJS bundled) + .filter((module) => !['zeromq', 'zeromqold', 'vscode-jsonrpc', 'vega-lite'].includes(module)) .map(async (module) => { const fullPath = require.resolve(module); diff --git a/build/fix-directory-imports.mjs b/build/fix-directory-imports.mjs deleted file mode 100644 index 8cea626f1a..0000000000 --- a/build/fix-directory-imports.mjs +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env node -// Script to fix directory imports - convert './foo' to './foo/index.js' where foo is a directory - -import { promises as fs } from 'fs'; -import path from 'path'; -import { fileURLToPath } from 'url'; -import { dirname } from 'path'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -const rootDir = path.join(__dirname, '..'); -const srcDir = path.join(rootDir, 'src'); - -// Known directory imports that need to be converted to /index.js -const directoryImports = [ - 'platform/logging', - 'telemetry', - 'platform/pythonEnvironments/info', - 'standalone/api' -]; - -async function getAllTsFiles(dir) { - const files = []; - const entries = await fs.readdir(dir, { withFileTypes: true }); - - for (const entry of entries) { - const fullPath = path.join(dir, entry.name); - - if (entry.isDirectory()) { - if (!['node_modules', 'out', 'dist', '.git', '.vscode', 'resources'].includes(entry.name)) { - files.push(...(await getAllTsFiles(fullPath))); - } - } else if (entry.name.endsWith('.ts') || entry.name.endsWith('.tsx')) { - files.push(fullPath); - } - } - - return files; -} - -function fixDirectoryImports(content) { - let modified = content; - let changeCount = 0; - - for (const dirImport of directoryImports) { - // Match imports with any relative path prefix (./, ../, ../../foo/, etc.) - // Captures: quote, prefix (e.g., './', '../../'), directoryImport, '.js', quote - const pattern = new RegExp(`(['"])((?:\\.\\.\\/|\\.\\/)(?:.*\\/)?)${dirImport}\\.js(['"])`, 'g'); - - const newModified = modified.replace(pattern, (match, quote1, prefix, quote2) => { - changeCount++; - return `${quote1}${prefix}${dirImport}/index.js${quote2}`; - }); - modified = newModified; - } - - return { content: modified, changed: changeCount > 0, changeCount }; -} - -async function main() { - console.log('šŸ” Finding all TypeScript files in src/...'); - const tsFiles = await getAllTsFiles(srcDir); - console.log(`šŸ“ Found ${tsFiles.length} TypeScript files\n`); - - let totalFilesChanged = 0; - let totalImportsFixed = 0; - - for (const file of tsFiles) { - const content = await fs.readFile(file, 'utf-8'); - const { content: newContent, changed, changeCount } = fixDirectoryImports(content); - - if (changed) { - await fs.writeFile(file, newContent, 'utf-8'); - totalFilesChanged++; - totalImportsFixed += changeCount; - const relativePath = path.relative(rootDir, file); - console.log(`āœ… ${relativePath} (${changeCount} import${changeCount > 1 ? 's' : ''})`); - } - } - - console.log(`\n✨ Done!`); - console.log(`šŸ“Š Modified ${totalFilesChanged} files`); - console.log(`šŸ”— Fixed ${totalImportsFixed} directory import${totalImportsFixed !== 1 ? 's' : ''}`); -} - -main().catch(error => { - console.error('āŒ Error:', error); - process.exit(1); -}); diff --git a/build/mocha-esm-loader.js b/build/mocha-esm-loader.js index 8e1d39d51a..153fdc306a 100644 --- a/build/mocha-esm-loader.js +++ b/build/mocha-esm-loader.js @@ -13,6 +13,40 @@ const projectRoot = path.join(__dirname, '..'); const vscodeMockPath = pathToFileURL(path.join(projectRoot, 'out/test/vscode-mock.js')).href; const telemetryMockPath = pathToFileURL(path.join(projectRoot, 'out/test/mocks/vsc/telemetryReporter.js')).href; +// Mock source for @vscode/python-extension - used in multiple places +const PYTHON_EXTENSION_MOCK = ` + // Mock PythonExtension class + export class PythonExtension { + static api() { + return Promise.resolve({ + environments: { + known: [], + resolveEnvironment: () => Promise.resolve(undefined), + onDidChangeEnvironments: { event: () => ({ dispose: () => {} }) } + } + }); + } + } + + // Mock Environment type (empty object, just for type compatibility) + export const Environment = {}; + + // Mock EnvironmentPath type + export const EnvironmentPath = {}; + + // Mock ResolvedEnvironment type + export const ResolvedEnvironment = {}; + + // Mock KnownEnvironmentTools + export const KnownEnvironmentTools = {}; + + // Mock KnownEnvironmentTypes + export const KnownEnvironmentTypes = {}; + + // Extension ID constant + export const PVSC_EXTENSION_ID = 'ms-python.python'; +`; + import { stat } from 'node:fs/promises'; /** @@ -78,6 +112,16 @@ export async function resolve(specifier, context, nextResolve) { }; } + // Intercept @vscode/python-extension - needed because it requires VS Code runtime + // Note: Only exact match is needed - no subpath imports (e.g., '@vscode/python-extension/foo') + // exist in the codebase. If subpath imports are added, change to startsWith() match. + if (specifier === '@vscode/python-extension') { + return { + url: 'vscode-mock:///python-extension', + shortCircuit: true + }; + } + // Handle extensionless imports (both relative and node_modules) return resolveWithFallback(specifier, context, nextResolve); } @@ -346,10 +390,57 @@ export async function load(url, context, nextLoad) { }; } + // Handle @vscode/python-extension mock - needed because it requires VS Code runtime + if (moduleName === 'python-extension') { + return { + format: 'module', + source: PYTHON_EXTENSION_MOCK, + shortCircuit: true + }; + } + // Unknown vscode-mock module throw new Error(`Unknown vscode-mock module: ${moduleName}`); } + // Handle @vscode/python-extension - it's a CJS module that esmock tries to load directly + // We intercept and return our mock to avoid CJS/ESM compatibility issues + // Use proper URL parsing and decoding to handle all encoding variants + try { + const urlObj = new URL(url); + const decodedPath = decodeURIComponent(urlObj.pathname); + + if (decodedPath.includes('@vscode/python-extension') || decodedPath.includes('/@vscode/python-extension')) { + return { + format: 'module', + source: PYTHON_EXTENSION_MOCK, + shortCircuit: true + }; + } + } catch { + // If URL parsing fails, fall through to other handlers + } + + // For .js files in the out/ directory, inject CJS globals shims + // This is needed because source files use native __dirname, __filename, require which don't exist in ESM + if (url.startsWith('file://') && url.includes('/out/') && url.endsWith('.js')) { + const filePath = fileURLToPath(url); + const fs = await import('node:fs/promises'); + const source = await fs.readFile(filePath, 'utf8'); + const shim = `import { fileURLToPath as __fileURLToPath } from 'url'; +import { dirname as __getDirname } from 'path'; +import { createRequire as __createRequire } from 'module'; +const __filename = __fileURLToPath(import.meta.url); +const __dirname = __getDirname(__filename); +const require = __createRequire(import.meta.url); +`; + return { + format: 'module', + source: shim + source, + shortCircuit: true + }; + } + // Let Node.js handle all other URLs return nextLoad(url, context); } diff --git a/build/remove-js-extensions.mjs b/build/remove-js-extensions.mjs deleted file mode 100644 index a1a47c8d69..0000000000 --- a/build/remove-js-extensions.mjs +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env node -// Script to remove .js extensions from relative imports in TypeScript files -// This is for bundler-based module resolution where extensions are not needed - -import { promises as fs } from 'fs'; -import path from 'path'; -import { fileURLToPath } from 'url'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -const rootDir = path.join(__dirname, '..'); -const srcDir = path.join(rootDir, 'src'); - -async function getAllTsFiles(dir) { - const files = []; - const entries = await fs.readdir(dir, { withFileTypes: true }); - - for (const entry of entries) { - const fullPath = path.join(dir, entry.name); - - if (entry.isDirectory()) { - if (!['node_modules', 'out', 'dist', '.git', '.vscode', 'resources'].includes(entry.name)) { - files.push(...(await getAllTsFiles(fullPath))); - } - } else if ((entry.name.endsWith('.ts') || entry.name.endsWith('.tsx')) && !entry.name.endsWith('.d.ts')) { - files.push(fullPath); - } - } - - return files; -} - -function removeJsExtensions(content) { - let modified = content; - let changeCount = 0; - - // Pattern to match relative imports with .js extension - // Matches: from './foo.js' or from '../bar/index.js' - // Does NOT match: from 'package-name' or from 'node:fs' - const patterns = [ - // import ... from './path.js' or '../path.js' - /(\bfrom\s+['"])(\.\/?[^'"]+)\.js(['"])/g, - // import('./path.js') or import('../path.js') - /(\bimport\s*\(\s*['"])(\.\/?[^'"]+)\.js(['"])/g - ]; - - for (const pattern of patterns) { - modified = modified.replace(pattern, (match, before, importPath, after) => { - changeCount++; - return `${before}${importPath}${after}`; - }); - } - - return { content: modified, changed: changeCount > 0, changeCount }; -} - -async function main() { - console.log('šŸ” Finding all TypeScript files in src/...'); - const tsFiles = await getAllTsFiles(srcDir); - console.log(`šŸ“ Found ${tsFiles.length} TypeScript files\n`); - - let totalFilesChanged = 0; - let totalExtensionsRemoved = 0; - - for (const file of tsFiles) { - const content = await fs.readFile(file, 'utf-8'); - const { content: newContent, changed, changeCount } = removeJsExtensions(content); - - if (changed) { - await fs.writeFile(file, newContent, 'utf-8'); - totalFilesChanged++; - totalExtensionsRemoved += changeCount; - const relativePath = path.relative(rootDir, file); - console.log(`āœ… ${relativePath} (${changeCount} extension${changeCount > 1 ? 's' : ''})`); - } - } - - console.log(`\n✨ Done!`); - console.log(`šŸ“Š Modified ${totalFilesChanged} files`); - console.log(`šŸ”— Removed ${totalExtensionsRemoved} .js extension${totalExtensionsRemoved !== 1 ? 's' : ''}`); -} - -main().catch((error) => { - console.error('āŒ Error:', error); - process.exit(1); -}); diff --git a/package-lock.json b/package-lock.json index becead4792..1be4df041e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,7 @@ "isomorphic-ws": "^4.0.1", "jquery": "^3.6.0", "js-yaml": "^4.1.1", - "jsonc-parser": "^2.0.3", + "jsonc-parser": "3.3.1", "lodash": "^4.17.21", "marked": "^4.0.10", "node-fetch": "^2.6.7", @@ -17352,9 +17352,10 @@ } }, "node_modules/jsonc-parser": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", - "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==" + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" }, "node_modules/jsonfile": { "version": "4.0.0", @@ -39838,9 +39839,9 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" }, "jsonc-parser": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", - "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==" + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==" }, "jsonfile": { "version": "4.0.0", diff --git a/package.json b/package.json index 77c50472bb..f314a4af85 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,6 @@ "name": "vscode-deepnote", "displayName": "Deepnote", "version": "1.2.0", - "type": "module", "description": "Deepnote notebook support.", "publisher": "Deepnote", "author": { @@ -62,7 +61,7 @@ "onNotebook:deepnote", "onNotebook:interactive" ], - "main": "./dist/extension.node.proxy.js", + "main": "./dist/extension.node.js", "browser": "./dist/extension.web.bundle.js", "capabilities": { "virtualWorkspaces": true, @@ -2533,7 +2532,7 @@ "isomorphic-ws": "^4.0.1", "jquery": "^3.6.0", "js-yaml": "^4.1.1", - "jsonc-parser": "^2.0.3", + "jsonc-parser": "3.3.1", "lodash": "^4.17.21", "marked": "^4.0.10", "node-fetch": "^2.6.7", diff --git a/src/extension.node.proxy.ts b/src/extension.node.proxy.ts deleted file mode 100644 index 0922f2ccf8..0000000000 --- a/src/extension.node.proxy.ts +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import type { IExtensionApi } from './standalone/api'; -import type { IExtensionContext } from './platform/common/types'; -import { ExtensionMode } from 'vscode'; - -let realEntryPoint: { - activate: typeof activate; - deactivate: typeof deactivate; -}; - -export async function activate(context: IExtensionContext): Promise { - // Use dynamic path construction to prevent esbuild from bundling the module - // Must use explicit './' prefix for ESM imports to work correctly - const entryPoint = - context.extensionMode === ExtensionMode.Test ? '../out/extension.node.js' : './extension.node.js'; - try { - realEntryPoint = await import(/* webpackIgnore: true */ entryPoint); - return realEntryPoint.activate(context); - } catch (ex) { - if (!ex.toString().includes(`Cannot find module '../out/extension.node.js'`)) { - console.error('Failed to activate extension, falling back to `./extension.node.js`', ex); - } - // In smoke tests, we do not want to load the out/extension.node. - const fallbackPath = './extension.node.js'; - realEntryPoint = await import(/* webpackIgnore: true */ fallbackPath); - return realEntryPoint.activate(context); - } -} - -export function deactivate(): Thenable { - return realEntryPoint.deactivate(); -} diff --git a/src/kernels/deepnote/deepnoteLspClientManager.node.ts b/src/kernels/deepnote/deepnoteLspClientManager.node.ts index 227e29bda2..8389da4bad 100644 --- a/src/kernels/deepnote/deepnoteLspClientManager.node.ts +++ b/src/kernels/deepnote/deepnoteLspClientManager.node.ts @@ -2,7 +2,6 @@ import * as fs from 'fs'; import * as vscode from 'vscode'; import { CancellationError } from 'vscode'; import { inject, injectable } from 'inversify'; -import { createRequire } from 'module'; import type { LanguageClient as LanguageClientType, LanguageClientOptions, @@ -10,9 +9,8 @@ import type { ServerOptions } from 'vscode-languageclient/node'; -// Use createRequire for ESM compatibility with vscode-languageclient // The bundled module uses ESM default export, so we need to access .default -const require = createRequire(import.meta.url); +// eslint-disable-next-line @typescript-eslint/no-require-imports const languageClientModule = require('vscode-languageclient/node'); const { LanguageClient, TransportKind, RevealOutputChannelOn } = (languageClientModule.default ?? languageClientModule) as typeof import('vscode-languageclient/node'); @@ -543,6 +541,7 @@ export class DeepnoteLspClientManager private getSqlLanguageServerModule(): string { // Try require.resolve first - this handles different package layouts (works in dev mode) try { + // eslint-disable-next-line @typescript-eslint/no-require-imports const serverModule = require.resolve('@deepnote/sql-language-server/dist/bin/vscodeExtensionServer.js'); logger.trace('SQL LSP server module resolved via require.resolve:', serverModule); diff --git a/src/kernels/raw/finder/jupyterPaths.node.unit.test.ts b/src/kernels/raw/finder/jupyterPaths.node.unit.test.ts index cb3b9a0505..de268848a7 100644 --- a/src/kernels/raw/finder/jupyterPaths.node.unit.test.ts +++ b/src/kernels/raw/finder/jupyterPaths.node.unit.test.ts @@ -18,7 +18,6 @@ import { ICustomEnvironmentVariablesProvider } from '../../../platform/common/va import { IPythonExecutionService, IPythonExecutionFactory } from '../../../platform/interpreter/types.node'; import { PythonEnvironment } from '../../../platform/pythonEnvironments/info'; import * as path from '../../../platform/vscode-path/path'; -import { getFilename } from '../../../platform/common/esmUtils.node'; import { EXTENSION_ROOT_DIR_FOR_TESTS } from '../../../test/constants.node'; import { resolvableInstance, uriEquals } from '../../../test/datascience/helpers'; import { IInterpreterService } from '../../../platform/interpreter/contracts'; @@ -338,7 +337,7 @@ suite('Jupyter Paths', () => { when(platformService.osType).thenReturn(OSType.Windows); when(platformService.homeDir).thenReturn(windowsHomeDir); when(memento.get(CACHE_KEY_FOR_JUPYTER_KERNEL_PATHS, anything())).thenReturn([]); - const jupyter_Paths = [getFilename(import.meta.url)]; + const jupyter_Paths = [__filename]; process.env['JUPYTER_PATH'] = jupyter_Paths.join(path.delimiter); const paths = await jupyterPaths.getKernelSpecRootPaths(cancelToken.token); @@ -348,10 +347,7 @@ suite('Jupyter Paths', () => { assert.strictEqual(paths.length, 3, `Expected 3 paths, got ${paths.length}, ${JSON.stringify(paths)}`); // First path should be from JUPYTER_PATH - assert.strictEqual( - paths[0].toString(), - Uri.joinPath(Uri.file(getFilename(import.meta.url)), 'kernels').toString() - ); + assert.strictEqual(paths[0].toString(), Uri.joinPath(Uri.file(__filename), 'kernels').toString()); // Second path should be from data directory (.jupyter/data/kernels) assert.strictEqual(paths[1].toString(), Uri.joinPath(windowsHomeDir, '.jupyter', 'data', 'kernels').toString()); @@ -363,7 +359,7 @@ suite('Jupyter Paths', () => { when(platformService.osType).thenReturn(OSType.Windows); when(platformService.homeDir).thenReturn(windowsHomeDir); when(memento.get(CACHE_KEY_FOR_JUPYTER_KERNEL_PATHS, anything())).thenReturn([]); - const jupyter_Paths = [getFilename(import.meta.url)]; + const jupyter_Paths = [__filename]; process.env['JUPYTER_PATH'] = jupyter_Paths.join(path.delimiter); const allUserProfilePath = (process.env['PROGRAMDATA'] = path.join(EXTENSION_ROOT_DIR_FOR_TESTS, 'temp')); @@ -374,10 +370,7 @@ suite('Jupyter Paths', () => { assert.strictEqual(paths.length, 4, `Expected 4 paths, got ${paths.length}, ${JSON.stringify(paths)}`); // First path should be from JUPYTER_PATH - assert.strictEqual( - paths[0].toString(), - Uri.joinPath(Uri.file(getFilename(import.meta.url)), 'kernels').toString() - ); + assert.strictEqual(paths[0].toString(), Uri.joinPath(Uri.file(__filename), 'kernels').toString()); // Second path should be from data directory (.jupyter/data/kernels) assert.strictEqual(paths[1].toString(), Uri.joinPath(windowsHomeDir, '.jupyter', 'data', 'kernels').toString()); diff --git a/src/kernels/raw/launcher/kernelLauncher.unit.test.ts b/src/kernels/raw/launcher/kernelLauncher.unit.test.ts index 4d0f6bad82..2284e1ea8c 100644 --- a/src/kernels/raw/launcher/kernelLauncher.unit.test.ts +++ b/src/kernels/raw/launcher/kernelLauncher.unit.test.ts @@ -29,9 +29,6 @@ import { KernelProcess } from './kernelProcess.node'; import { IPythonExecutionFactory, IPythonExecutionService } from '../../../platform/interpreter/types.node'; import { UsedPorts } from '../../common/usedPorts'; import { mockedVSCodeNamespaces, resetVSCodeMocks } from '../../../test/vscode-mock'; -import { getDirname } from '../../../platform/common/esmUtils.node'; - -const __dirname = getDirname(import.meta.url); suite('kernel Launcher', () => { let disposables: IDisposable[] = []; diff --git a/src/kernels/raw/launcher/kernelProcess.node.unit.test.ts b/src/kernels/raw/launcher/kernelProcess.node.unit.test.ts index ff614303cc..3d7991bdd3 100644 --- a/src/kernels/raw/launcher/kernelProcess.node.unit.test.ts +++ b/src/kernels/raw/launcher/kernelProcess.node.unit.test.ts @@ -38,9 +38,7 @@ import { waitForCondition } from '../../../test/common.node'; import { IS_REMOTE_NATIVE_TEST } from '../../../test/constants'; import { logger } from '../../../platform/logging'; import { IPlatformService } from '../../../platform/common/platform/types'; -import { getDirname } from '../../../platform/common/esmUtils.node'; -const __dirname = getDirname(import.meta.url); import { IPythonExecutionFactory, IPythonExecutionService } from '../../../platform/interpreter/types.node'; import { createObservable } from '../../../platform/common/process/proc.node'; import { ServiceContainer } from '../../../platform/ioc/container'; diff --git a/src/kernels/raw/session/rawSessionConnection.node.unit.test.ts b/src/kernels/raw/session/rawSessionConnection.node.unit.test.ts index 2b42dbe4f8..3ab48b0b03 100644 --- a/src/kernels/raw/session/rawSessionConnection.node.unit.test.ts +++ b/src/kernels/raw/session/rawSessionConnection.node.unit.test.ts @@ -31,12 +31,9 @@ import { KernelConnectionTimeoutError } from '../../errors/kernelConnectionTimeo import { mockedVSCodeNamespaces, resetVSCodeMocks } from '../../../test/vscode-mock'; import type { IFileSystem } from '../../../platform/common/platform/types'; import { computeLocalWorkingDirectory } from './kernelWorkingDirectory.node'; -import { createRequire } from 'module'; -import { getDirname } from '../../../platform/common/esmUtils.node'; import esmock from 'esmock'; -const __dirname = getDirname(import.meta.url); -const require = createRequire(import.meta.url); +// eslint-disable-next-line @typescript-eslint/no-require-imports const jupyterLabKernel = require('@jupyterlab/services/lib/kernel/default') as typeof import('@jupyterlab/services/lib/kernel/default'); diff --git a/src/kernels/raw/session/zeromq.node.ts b/src/kernels/raw/session/zeromq.node.ts index 17fb07e108..0c9023852a 100644 --- a/src/kernels/raw/session/zeromq.node.ts +++ b/src/kernels/raw/session/zeromq.node.ts @@ -9,13 +9,11 @@ import { Telemetry, sendTelemetryEvent } from '../../../telemetry'; import { noop } from '../../../platform/common/utils/misc'; import { DistroInfo, getDistroInfo } from '../../../platform/common/platform/linuxDistro.node'; import { EXTENSION_ROOT_DIR } from '../../../platform/constants.node'; -import { createRequire } from 'module'; -// Use createRequire for dynamic loading of zeromq, which is a native module -const require = createRequire(import.meta.url); const zeromqModuleName = `${'zeromq'}`; export function getZeroMQ(): typeof import('zeromq') { try { + // eslint-disable-next-line @typescript-eslint/no-require-imports const zmq: typeof import('zeromq') = require(zeromqModuleName); // We do not want to block the process from exiting if there are any pending messages. zmq.context.blocky = false; @@ -23,6 +21,7 @@ export function getZeroMQ(): typeof import('zeromq') { return zmq; } catch (e) { try { + // eslint-disable-next-line @typescript-eslint/no-require-imports const zmq = require(path.join(EXTENSION_ROOT_DIR, 'dist', 'node_modules', 'zeromqold')); logger.info('ZMQ loaded via fallback mechanism.'); sendZMQTelemetry(false, true, e.message || e.toString()).catch(noop); diff --git a/src/notebooks/controllers/ipywidgets/scriptSourceProvider/localWidgetScriptSourceProvider.unit.test.ts b/src/notebooks/controllers/ipywidgets/scriptSourceProvider/localWidgetScriptSourceProvider.unit.test.ts index e95b6cf995..978e071a23 100644 --- a/src/notebooks/controllers/ipywidgets/scriptSourceProvider/localWidgetScriptSourceProvider.unit.test.ts +++ b/src/notebooks/controllers/ipywidgets/scriptSourceProvider/localWidgetScriptSourceProvider.unit.test.ts @@ -12,9 +12,6 @@ import { IIPyWidgetScriptManager } from '../types'; import { LocalWidgetScriptSourceProvider } from './localWidgetScriptSourceProvider.node'; -import { getDirname } from '../../../../platform/common/esmUtils.node'; - -const __dirname = getDirname(import.meta.url); /* eslint-disable , @typescript-eslint/no-explicit-any */ suite('ipywidget - Local Widget Script Source', () => { diff --git a/src/notebooks/controllers/ipywidgets/scriptSourceProvider/nbExtensionsPathProvider.unit.test.ts b/src/notebooks/controllers/ipywidgets/scriptSourceProvider/nbExtensionsPathProvider.unit.test.ts index c976cd3ad8..6a8611c3e4 100644 --- a/src/notebooks/controllers/ipywidgets/scriptSourceProvider/nbExtensionsPathProvider.unit.test.ts +++ b/src/notebooks/controllers/ipywidgets/scriptSourceProvider/nbExtensionsPathProvider.unit.test.ts @@ -23,9 +23,6 @@ import { NbExtensionsPathProvider as WebNbExtensionsPathProvider } from './nbExt import { PythonExtension } from '@vscode/python-extension'; import { resolvableInstance } from '../../../../test/datascience/helpers'; import { dispose } from '../../../../platform/common/utils/lifecycle'; -import { getDirname } from '../../../../platform/common/esmUtils.node'; - -const __dirname = getDirname(import.meta.url); [false, true].forEach((isWeb) => { const localNonPythonKernelSpec = LocalKernelSpecConnectionMetadata.create({ diff --git a/src/notebooks/deepnote/deepnoteDataConverter.ts b/src/notebooks/deepnote/deepnoteDataConverter.ts index 8f81d9c431..a9bffc365b 100644 --- a/src/notebooks/deepnote/deepnoteDataConverter.ts +++ b/src/notebooks/deepnote/deepnoteDataConverter.ts @@ -8,7 +8,7 @@ import { CodeBlockConverter } from './converters/codeBlockConverter'; import { addPocketToCellMetadata, createBlockFromPocket } from '../../platform/deepnote/pocket'; import { MarkdownBlockConverter } from './converters/markdownBlockConverter'; import { VisualizationBlockConverter } from './converters/visualizationBlockConverter'; -import { compile as convertVegaLiteSpecToVega } from 'vega-lite'; +import { compile as convertVegaLiteSpecToVega, ensureVegaLiteLoaded } from './vegaLiteWrapper'; import { produce } from 'immer'; import { SqlBlockConverter } from './converters/sqlBlockConverter'; import { TextBlockConverter } from './converters/textBlockConverter'; @@ -54,6 +54,14 @@ export class DeepnoteDataConverter { this.registry.register(new VisualizationBlockConverter()); } + /** + * Initialize async dependencies like vega-lite. + * Must be called before using output conversion methods. + */ + async initialize(): Promise { + await ensureVegaLiteLoaded(); + } + /** * Finds a converter for the given block type. * @param blockType The type of block to find a converter for @@ -395,8 +403,13 @@ export class DeepnoteDataConverter { draft.config.customFormatTypes = true; }); - const vegaSpec = convertVegaLiteSpecToVega(patchedVegaLiteSpec as VegaLiteSpec).spec; - items.push(NotebookCellOutputItem.json(vegaSpec, 'application/vnd.vega.v6+json')); + const vegaResult = convertVegaLiteSpecToVega(patchedVegaLiteSpec as VegaLiteSpec); + + if (vegaResult) { + items.push( + NotebookCellOutputItem.json(vegaResult.spec, 'application/vnd.vega.v6+json') + ); + } } if (data['application/json']) { diff --git a/src/notebooks/deepnote/deepnoteSerializer.ts b/src/notebooks/deepnote/deepnoteSerializer.ts index aa3832613d..6216a57628 100644 --- a/src/notebooks/deepnote/deepnoteSerializer.ts +++ b/src/notebooks/deepnote/deepnoteSerializer.ts @@ -75,6 +75,13 @@ export class DeepnoteNotebookSerializer implements NotebookSerializer { throw new Error('Serialization cancelled'); } + // Initialize vega-lite for output conversion (lazy-loaded ESM module) + await this.converter.initialize(); + + if (token?.isCancellationRequested) { + throw new Error('Serialization cancelled'); + } + try { const contentString = new TextDecoder('utf-8').decode(content); const deepnoteFile = yaml.load(contentString) as DeepnoteFile; diff --git a/src/notebooks/deepnote/vegaLiteWrapper.ts b/src/notebooks/deepnote/vegaLiteWrapper.ts new file mode 100644 index 0000000000..f377c31abd --- /dev/null +++ b/src/notebooks/deepnote/vegaLiteWrapper.ts @@ -0,0 +1,60 @@ +// Wrapper for vega-lite that uses dynamic import to handle ESM/CJS interop +// vega-lite has top-level await (through vega-canvas) which isn't compatible with CJS bundles +// This wrapper makes vega-lite optional - if it fails to load, we gracefully degrade + +import type { TopLevelSpec } from 'vega-lite'; +import type { Spec as VegaSpec } from 'vega'; + +import { logger } from '../../platform/logging'; + +let compileFunction: ((spec: TopLevelSpec) => { spec: VegaSpec }) | null = null; +let loadPromise: Promise | null = null; +let loadFailed = false; + +async function loadVegaLite(): Promise { + if (typeof compileFunction === 'function' || loadFailed) return; + if (loadPromise) return loadPromise; + + loadPromise = (async () => { + try { + const vegaLite = await import('vega-lite'); + + if (!vegaLite.compile || typeof vegaLite.compile !== 'function') { + throw new Error( + `vega-lite module loaded but compile function is missing or not a function. Got: ${typeof vegaLite.compile}. Vega-lite chart conversion will be skipped.` + ); + } + + compileFunction = vegaLite.compile; + } catch (error) { + compileFunction = null; + loadFailed = true; + logger.warn('vega-lite could not be loaded. Vega-lite chart conversion will be skipped.', error); + } + })(); + + return loadPromise; +} + +/** + * Check if vega-lite is available for use. + */ +export function isVegaLiteAvailable(): boolean { + return typeof compileFunction === 'function'; +} + +/** + * Compile a vega-lite spec to a vega spec. + * Returns undefined if vega-lite is not available. + */ +export function compile(spec: TopLevelSpec): { spec: VegaSpec } | undefined { + if (typeof compileFunction !== 'function') { + return undefined; + } + + return compileFunction(spec); +} + +export async function ensureVegaLiteLoaded(): Promise { + await loadVegaLite(); +} diff --git a/src/notebooks/notebookEnvironmentService.node.ts b/src/notebooks/notebookEnvironmentService.node.ts index 3cfdc05cba..28ad68f62d 100644 --- a/src/notebooks/notebookEnvironmentService.node.ts +++ b/src/notebooks/notebookEnvironmentService.node.ts @@ -16,7 +16,6 @@ import { getCachedEnvironment, getInterpreterInfo } from '../platform/interprete import type { Environment, EnvironmentPath } from '@vscode/python-extension'; import type { PythonEnvironment } from '../platform/pythonEnvironments/info'; import { toPythonSafePath } from '../platform/common/utils/encoder'; -import { getFilename } from '../platform/common/esmUtils.node'; @injectable() export class NotebookPythonEnvironmentService extends DisposableBase implements INotebookPythonEnvironmentService { @@ -154,7 +153,7 @@ import os as _VSCODE_os import sys as _VSCODE_sys import builtins as _VSCODE_builtins -if _VSCODE_os.path.exists(${toPythonSafePath(getFilename(import.meta.url))}): +if _VSCODE_os.path.exists(${toPythonSafePath(__filename)}): _VSCODE_builtins.print(f"EXECUTABLE{_VSCODE_sys.executable}EXECUTABLE") del _VSCODE_os, _VSCODE_sys, _VSCODE_builtins diff --git a/src/platform/common/esmUtils.node.ts b/src/platform/common/esmUtils.node.ts deleted file mode 100644 index d05c807d75..0000000000 --- a/src/platform/common/esmUtils.node.ts +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Deepnote. All rights reserved. -// Licensed under the MIT License. - -import { fileURLToPath } from 'url'; -// eslint-disable-next-line local-rules/node-imports -import { dirname } from 'path'; - -/** - * Get the directory name equivalent to __dirname in ESM context. - * @param importMetaUrl - Pass import.meta.url - * @returns The directory path - */ -export function getDirname(importMetaUrl: string): string { - return dirname(fileURLToPath(importMetaUrl)); -} - -/** - * Get the file name equivalent to __filename in ESM context. - * @param importMetaUrl - Pass import.meta.url - * @returns The file path - */ -export function getFilename(importMetaUrl: string): string { - return fileURLToPath(importMetaUrl); -} diff --git a/src/platform/common/variables/systemVariables.node.ts b/src/platform/common/variables/systemVariables.node.ts index e410fea45f..94a741a61a 100644 --- a/src/platform/common/variables/systemVariables.node.ts +++ b/src/platform/common/variables/systemVariables.node.ts @@ -7,7 +7,6 @@ import * as path from '../../vscode-path/path'; import { Uri, Range, workspace, window } from 'vscode'; import { AbstractSystemVariables } from './systemVariables'; import { getUserHomeDir } from '../utils/platform.node'; -import { getDirname } from '../esmUtils.node'; /** * System variables for node.js. Node specific is necessary because of using the current process environment. @@ -24,9 +23,7 @@ export class SystemVariables extends AbstractSystemVariables { constructor(file: Uri | undefined, rootFolder: Uri | undefined) { super(); const workspaceFolder = file ? workspace.getWorkspaceFolder(file) : undefined; - this._workspaceFolder = workspaceFolder - ? workspaceFolder.uri.fsPath - : rootFolder?.fsPath || getDirname(import.meta.url); + this._workspaceFolder = workspaceFolder ? workspaceFolder.uri.fsPath : rootFolder?.fsPath || __dirname; this._workspaceFolderName = path.basename(this._workspaceFolder); this._fileWorkspaceFolder = this._workspaceFolder; this._filePath = file ? file.fsPath : undefined; diff --git a/src/platform/constants.node.ts b/src/platform/constants.node.ts index 8e19a4f792..b8f1b5613d 100644 --- a/src/platform/constants.node.ts +++ b/src/platform/constants.node.ts @@ -2,12 +2,6 @@ // Licensed under the MIT License. import * as path from './vscode-path/path'; -import { createRequire } from 'module'; - -import { getDirname } from './common/esmUtils.node'; - -const require = createRequire(import.meta.url); -const __dirname = getDirname(import.meta.url); // We always use esbuild to bundle the extension, // Thus __dirname will always be a file in `dist` folder. @@ -19,6 +13,7 @@ export * from './constants'; // Override isPreReleaseVersion with Node.js-specific implementation export function isPreReleaseVersion(): boolean { try { + // eslint-disable-next-line @typescript-eslint/no-require-imports const { isPreRelease } = require('vscode-jupyter-release-version') as { isPreRelease?: boolean }; return isPreRelease === true; } catch { diff --git a/src/platform/interpreter/filter/completionProvider.node.ts b/src/platform/interpreter/filter/completionProvider.node.ts index 3f994b80eb..40ce0213f3 100644 --- a/src/platform/interpreter/filter/completionProvider.node.ts +++ b/src/platform/interpreter/filter/completionProvider.node.ts @@ -75,6 +75,9 @@ export class PythonEnvFilterCompletionProvider implements CompletionItemProvider const settings = document.getText(); const location = getLocation(settings, document.offsetAt(position)); const root = parseTree(settings); + if (!root) { + return []; + } const settingsNode = findNodeAtLocation(root, [location.path[0]]); if (!settingsNode) { return []; diff --git a/src/test/analysisEngineTest.node.ts b/src/test/analysisEngineTest.node.ts index c0377950c5..125fbca1f2 100644 --- a/src/test/analysisEngineTest.node.ts +++ b/src/test/analysisEngineTest.node.ts @@ -3,9 +3,6 @@ /* eslint-disable no-console, @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */ import * as path from '../platform/vscode-path/path'; -import { getDirname } from '../platform/common/esmUtils.node'; - -const __dirname = getDirname(import.meta.url); process.env.CODE_TESTS_WORKSPACE = path.join(__dirname, '..', '..', 'src', 'test'); process.env.IS_CI_SERVER_TEST_DEBUGGER = ''; diff --git a/src/test/common.node.ts b/src/test/common.node.ts index 21818f75ae..d5c961222f 100644 --- a/src/test/common.node.ts +++ b/src/test/common.node.ts @@ -20,9 +20,6 @@ import * as vscode from 'vscode'; import * as configSettings from '../platform/common/configSettings'; import * as initializeModule from './initialize.node'; import StreamZip from 'node-stream-zip'; -import { getDirname } from '../platform/common/esmUtils.node'; - -const __dirname = getDirname(import.meta.url); export { createEventHandler } from './common'; diff --git a/src/test/common/variables/envVarsService.vscode.test.ts b/src/test/common/variables/envVarsService.vscode.test.ts index ce424efc95..bb83302786 100644 --- a/src/test/common/variables/envVarsService.vscode.test.ts +++ b/src/test/common/variables/envVarsService.vscode.test.ts @@ -10,9 +10,6 @@ import { FileSystem } from '../../../platform/common/platform/fileSystem.node'; import { EnvironmentVariablesService } from '../../../platform/common/variables/environment.node'; import { IEnvironmentVariablesService } from '../../../platform/common/variables/types'; import { initialize } from '../../initialize'; -import { getDirname } from '../../../platform/common/esmUtils.node'; - -const __dirname = getDirname(import.meta.url); use(chaiAsPromised); diff --git a/src/test/constants.node.ts b/src/test/constants.node.ts index 88e5da60c6..7ec3de79dc 100644 --- a/src/test/constants.node.ts +++ b/src/test/constants.node.ts @@ -4,12 +4,9 @@ import * as path from '../platform/vscode-path/path'; import { setCI, setTestExecution, setUnitTestExecution } from '../platform/common/constants'; import { setTestSettings } from './constants'; -import { getDirname } from '../platform/common/esmUtils.node'; export * from './constants'; // Activating extension for Multiroot and Debugger CI tests for Windows takes just over 2 minutes sometimes, so 3 minutes seems like a safe margin - -const __dirname = getDirname(import.meta.url); export const EXTENSION_ROOT_DIR_FOR_TESTS = path.join(__dirname, '..', '..'); export const EXTENSION_TEST_DIR_FOR_FILES = path.join( EXTENSION_ROOT_DIR_FOR_TESTS, diff --git a/src/test/debuggerTest.node.ts b/src/test/debuggerTest.node.ts index 912007422f..823b85f87d 100644 --- a/src/test/debuggerTest.node.ts +++ b/src/test/debuggerTest.node.ts @@ -6,9 +6,6 @@ import * as path from '../platform/vscode-path/path'; import { runTests } from '@vscode/test-electron'; import { EXTENSION_ROOT_DIR_FOR_TESTS } from './constants.node'; -import { getDirname } from '../platform/common/esmUtils.node'; - -const __dirname = getDirname(import.meta.url); const workspacePath = path.join(__dirname, '..', '..', 'src', 'test', 'testMultiRootWkspc', 'multi.code-workspace'); process.env.IS_CI_SERVER_TEST_DEBUGGER = '1'; diff --git a/src/test/extension.serviceRegistry.vscode.test.ts b/src/test/extension.serviceRegistry.vscode.test.ts index 5e71b2b3f9..93584d4f4b 100644 --- a/src/test/extension.serviceRegistry.vscode.test.ts +++ b/src/test/extension.serviceRegistry.vscode.test.ts @@ -10,9 +10,6 @@ import * as ts from 'typescript'; import * as fs from 'fs-extra'; import glob from 'glob'; import * as path from '../platform/vscode-path/path'; -import { getDirname } from '../platform/common/esmUtils.node'; - -const __dirname = getDirname(import.meta.url); import { initialize } from './initialize.node'; import { interfaces } from 'inversify/lib/interfaces/interfaces'; diff --git a/src/test/index.node.ts b/src/test/index.node.ts index 24fa6cc74b..b6092000f4 100644 --- a/src/test/index.node.ts +++ b/src/test/index.node.ts @@ -28,9 +28,6 @@ import { stopJupyterServer } from './datascience/notebook/helper.node'; import { initialize } from './initialize.node'; import { rootHooks } from './testHooks.node'; import { isCI } from '../platform/common/constants'; -import { getDirname } from '../platform/common/esmUtils.node'; - -const __dirname = getDirname(import.meta.url); type SetupOptions = Mocha.MochaOptions & { testFilesSuffix: string; diff --git a/src/test/pythonEnvironments/constants.ts b/src/test/pythonEnvironments/constants.ts index b4b8579aa1..550311b3b3 100644 --- a/src/test/pythonEnvironments/constants.ts +++ b/src/test/pythonEnvironments/constants.ts @@ -4,9 +4,7 @@ /* eslint-disable local-rules/dont-use-filename */ import * as path from '../../platform/vscode-path/path'; -import { getDirname } from '../../platform/common/esmUtils.node'; -const __dirname = getDirname(import.meta.url); export const TEST_LAYOUT_ROOT = path.join(__dirname, '..', '..', '..', 'src', 'test', 'pythonEnvironments', 'common', 'envlayouts'); diff --git a/src/test/smokeTest.node.ts b/src/test/smokeTest.node.ts index 7406ea7065..c182f702e9 100644 --- a/src/test/smokeTest.node.ts +++ b/src/test/smokeTest.node.ts @@ -12,9 +12,6 @@ import * as fs from 'fs-extra'; import * as path from '../platform/vscode-path/path'; import { unzip } from './common.node'; import { EXTENSION_ROOT_DIR_FOR_TESTS, SMOKE_TEST_EXTENSIONS_DIR } from './constants.node'; -import { getDirname } from '../platform/common/esmUtils.node'; - -const __dirname = getDirname(import.meta.url); class TestRunner { public async start() { diff --git a/src/test/standardTest.node.ts b/src/test/standardTest.node.ts index 58ec757759..e36da9f80c 100644 --- a/src/test/standardTest.node.ts +++ b/src/test/standardTest.node.ts @@ -10,9 +10,6 @@ import * as tmp from 'tmp'; import { PythonExtension, setTestExecution, RendererExtension, isCI } from '../platform/common/constants'; import { DownloadPlatform } from '@vscode/test-electron/out/download'; import { arch } from 'os'; -import { getDirname } from '../platform/common/esmUtils.node'; - -const __dirname = getDirname(import.meta.url); // Support for passing grep (specially for models or Copilot Coding Agent) // Local Copilot or Copilot Coding Agent can use `--grep=XYZ` or `--grep XYZ` diff --git a/src/test/testRunner.ts b/src/test/testRunner.ts index 88fc7982e9..621b0ab184 100644 --- a/src/test/testRunner.ts +++ b/src/test/testRunner.ts @@ -12,14 +12,9 @@ import { MAX_EXTENSION_ACTIVATION_TIME } from './constants.node'; import { noop } from './core'; import { stopJupyterServer } from './datascience/notebook/helper.node'; import { initialize } from './initialize.node'; -import { createRequire } from 'module'; -import { getDirname } from '../platform/common/esmUtils.node'; - -const __dirname = getDirname(import.meta.url); // Linux: prevent a weird NPE when mocha on Linux requires the window size from the TTY. // Since we are not running in a tty environment, we just implement the method statically. -const require = createRequire(import.meta.url); const tty = require('tty'); if (!tty.getWindowSize) { tty.getWindowSize = function (): number[] { diff --git a/src/test/unittests.ts b/src/test/unittests.ts index 62c5c56d70..a624856322 100644 --- a/src/test/unittests.ts +++ b/src/test/unittests.ts @@ -5,8 +5,8 @@ import '../platform/ioc/reflectMetadata'; // Set up CommonJS require hooks for mocking vscode in CJS modules -import { createRequire } from 'module'; -const Module = createRequire(import.meta.url)('module'); +// eslint-disable-next-line @typescript-eslint/no-require-imports +const Module = require('module'); const originalLoad = Module._load; // We'll set up the hook after importing vscode-mock