diff --git a/.gitignore b/.gitignore index 04b5ac2a34c..7ec83e63f7c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .DS_Store .huskyrc.json +dist out *.exe log.log @@ -58,7 +59,8 @@ src/test/datascience/.venv* *.nls.*.json *.i18n.json l10n/ - +# Esbuild files +**/*.esbuild.meta.json # Compilation of less to css src/webviews/webview-side/interactive-common/variableExplorerGrid.css src/webviews/webview-side/interactive-common/variableExplorerGrid.css.map diff --git a/.vscode/launch.json b/.vscode/launch.json index 723a39c82ee..168bda8a2ed 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,7 +10,7 @@ "args": ["--extensionDevelopmentPath=${workspaceFolder}", "--enable-proposed-api"], "smartStep": true, "sourceMaps": true, - "outFiles": ["${workspaceFolder}/out/**/*", "!${workspaceFolder}/**/node_modules**/*"], + "outFiles": ["${workspaceFolder}/dist/**/*", "!${workspaceFolder}/**/node_modules**/*"], "preLaunchTask": "Compile", "skipFiles": ["/**"], "env": { @@ -34,7 +34,7 @@ "debugWebWorkerHost": true, "request": "launch", "args": ["--extensionDevelopmentPath=${workspaceFolder}", "--extensionDevelopmentKind=web"], - "outFiles": ["${workspaceFolder}/out/**/*", "!${workspaceFolder}/**/node_modules**/*"], + "outFiles": ["${workspaceFolder}/dist/**/*", "!${workspaceFolder}/**/node_modules**/*"], "preLaunchTask": "Compile Web Extension", "presentation": { "group": "1_extension", @@ -47,7 +47,7 @@ "debugWebWorkerHost": true, "request": "launch", "args": ["--extensionDevelopmentPath=${workspaceFolder}", "--extensionDevelopmentKind=web"], - "outFiles": ["${workspaceFolder}/out/**/*", "!${workspaceFolder}/**/node_modules**/*"], + "outFiles": ["${workspaceFolder}/dist/**/*", "!${workspaceFolder}/**/node_modules**/*"], "presentation": { "group": "1_extension", "order": 1 @@ -61,7 +61,7 @@ "args": ["--extensionDevelopmentPath=${workspaceFolder}", "--enable-proposed-api"], "smartStep": true, "sourceMaps": true, - "outFiles": ["${workspaceFolder}/out/**/*", "!${workspaceFolder}/**/node_modules**/*"], + "outFiles": ["${workspaceFolder}/dist/**/*", "!${workspaceFolder}/**/node_modules**/*"], "skipFiles": ["/**"], "env": { // Disable this to turoff on redux & console logging during debugging @@ -86,7 +86,7 @@ "args": ["--extensionDevelopmentPath=${workspaceFolder}", "${workspaceFolder}/data"], "smartStep": true, "sourceMaps": true, - "outFiles": ["${workspaceFolder}/out/**/*", "!${workspaceFolder}/**/node_modules**/*"], + "outFiles": ["${workspaceFolder}/dist/**/*", "!${workspaceFolder}/**/node_modules**/*"], "preLaunchTask": "Compile", "presentation": { "group": "1_extension", @@ -157,32 +157,6 @@ "order": 10 } }, - { - "name": "Perf Tests (*.perf.test.ts)", - "type": "extensionHost", - "request": "launch", - "runtimeExecutable": "${execPath}", - "args": [ - "${workspaceFolder}/src/test/testMultiRootWkspc/perfTests", - "--enable-proposed-api", - "--extensionDevelopmentPath=${workspaceFolder}", - "--extensionTestsPath=${workspaceFolder}/out/test/index.node.js" - ], - "env": { - "CI_PYTHON_PATH": "", - "VSC_JUPYTER_PERF_TEST": "1", - "TEST_FILES_SUFFIX": "notebookCellExecution.perf.test", - "VSC_JUPYTER_TEST_TIMEOUT": "60000" - }, - "sourceMaps": true, - "outFiles": ["${workspaceFolder}/out/**/*.js", "!${workspaceFolder}/**/node_modules**/*"], - "preLaunchTask": "Compile", - "skipFiles": ["/**"], - "presentation": { - "group": "2_tests", - "order": 12 - } - }, { "name": "Jedi LSP tests", "type": "extensionHost", diff --git a/.vscode/settings.json b/.vscode/settings.json index bfc7e311650..1c7cd82e00f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,8 @@ // Place your settings in this file to overwrite default and user settings. { "files.exclude": { - "out": false, // set this to true to hide the "out" folder with the compiled JS files + "out": false, + "dist": false, "**/*.pyc": true, ".nyc_output": true, "obj": true, @@ -17,7 +18,8 @@ "**/.ropeproject/**": true }, "search.exclude": { - "out": true, // set this to false to include "out" folder in search results + "out": true, + "dist": true, "**/node_modules": true, "coverage": true, "**/.venv/**": true, @@ -30,11 +32,11 @@ }, "[typescript]": { "editor.formatOnSave": true, - "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[javascript]": { "editor.formatOnSave": true, - "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.defaultFormatter": "esbenp.prettier-vscode" }, "git.branchProtection": ["main", "release*"], "git.branchProtectionPrompt": "alwaysCommitToNewBranch", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 5e0b8d73b7a..a59bee0407d 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -18,41 +18,6 @@ "isDefault": true } }, - { - "label": "Compile Viewers", - "type": "npm", - "script": "compile-viewers-watch", - "isBackground": true, - "group": { - "kind": "build", - "isDefault": true - }, - "problemMatcher": ["$esbuild-watch"] - }, - { - "label": "Compile Web Extension", - "type": "npm", - "script": "compile-web-watch", - "isBackground": true, - "group": { - "kind": "build", - "isDefault": true - }, - "problemMatcher": ["$ts-webpack-watch"] - }, - { - "label": "compile-web-test", - "type": "npm", - "script": "compile-web-test", - "dependsOrder": "sequence", - "dependsOn": ["Compile"], - "isBackground": true, - "group": { - "kind": "build", - "isDefault": true - }, - "problemMatcher": ["$ts-webpack-watch"] - }, { "label": "Run Unit Tests", "type": "npm", @@ -71,6 +36,7 @@ { "type": "npm", "script": "launchWebExtension", + "dependsOn": ["package.json.entry.dist"], "problemMatcher": [], "label": "Launch Web Extension (Chrome)" }, @@ -78,6 +44,7 @@ "type": "shell", "problemMatcher": [], "command": "npm", + "dependsOn": ["package.json.entry.dist"], "args": ["run", "launchWebExtension", "--", "--browser=webkit", "--port=3111"], "label": "Launch Web Extension (Safari)" }, diff --git a/.vscodeignore b/.vscodeignore index 2a852bb37fd..da310b0a3ac 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -1,6 +1,6 @@ **/*.map **/*.analyzer.html -!out/webviews/webview-side/common/node_modules/**/* +**/*.esbuild.meta.json images/** docs/** *.vsix @@ -81,30 +81,7 @@ i18n/** node_modules/** obj/** logs/** -out/**/*.stats.json -out/**/*.analyzer.html -out/coverconfig.json -out/webviews/webview-side/**/*.analyzer.html -out/webviews/webview-side/common/** -out/webviews/webview-side/**/*.js.LICENSE -out/webviews/webview-side/data-explorer/** -out/webviews/webview-side/webviews/webview-side/** -out/webviews/webview-side/history-react/** -out/webviews/webview-side/interactive-common/** -out/webviews/webview-side/ipywidgets/** -out/webviews/webview-side/native-editor/** -out/webviews/webview-side/notebook/webviews/webview-side/** -out/webviews/webview-side/notebook/ipywidgets/** -out/webviews/webview-side/notebook/index.*.html -out/webviews/webview-side/plot/** -out/webviews/webview-side/react-common/** -out/webviews/webview-side/viewers/webviews/webview-side/** -out/webviews/webview-side/viewers/ipywidgets/** -out/webviews/webview-side/viewers/index.*.html -out/webviews/webview-side/widgetTester/** -out/pythonFiles/** -out/src/** -out/test/** +out/** precommit.hook pythonFiles/**/*.pyc pythonFiles/lib/**/*.dist-info/** diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b0c0074ba42..fc10edc145c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,7 +46,7 @@ python -m pip --disable-pip-version-check install -t ./pythonFiles/lib/python -- ### Incremental Build -Run the `Compile`, `Compile Web Views`, and `Compile Web Extension` build Tasks from the [Run Build Task...](https://code.visualstudio.com/docs/editor/tasks) command picker (short cut `CTRL+SHIFT+B` or `⇧⌘B`). This will leave build tasks running in the background and which will re-run as files are edited and saved. You can see the output from either task in the Terminal panel (use the selector to choose which output to look at). +Run the `Compile` build Tasks from the [Run Build Task...](https://code.visualstudio.com/docs/editor/tasks) command picker (short cut `CTRL+SHIFT+B` or `⇧⌘B`). This will leave build tasks running in the background and which will re-run as files are edited and saved. You can see the output from either task in the Terminal panel (use the selector to choose which output to look at). You can also compile from the command-line. For a full compile you can use: @@ -58,10 +58,9 @@ For incremental builds you can use the following commands depending on your need ```shell npm run compile -npm run compile-viewers-watch # For Plot, Data Frame, Variable & IPyWidget Viewer ``` -Sometimes you will need to run `npm run clean` and even `rm -r out`. +Sometimes you will need to run `npm run clean` and even `rm -r out dist`. This is especially true if you have added or removed files. ### Errors and Warnings diff --git a/api/.gitignore b/api/.gitignore index 1555a9238a9..cc0e47cbd3f 100644 --- a/api/.gitignore +++ b/api/.gitignore @@ -1,4 +1,5 @@ .DS_Store +dist out **/node_modules bin/** diff --git a/build/esbuild/build.ts b/build/esbuild/build.ts index 6e267556eec..4af6515c279 100644 --- a/build/esbuild/build.ts +++ b/build/esbuild/build.ts @@ -6,13 +6,73 @@ import * as esbuild from 'esbuild'; import { green } from 'colors'; import type { BuildOptions, Charset, Loader, Plugin, SameShape } from 'esbuild'; import { lessLoader } from 'esbuild-plugin-less'; -import fs from 'fs'; -import { nodeModulesToExternalize } from '../webpack/common'; +import fs from 'fs-extra'; +import { getZeroMQPreBuildsFoldersToKeep, getBundleConfiguration, bundleConfiguration } from '../webpack/common'; +// These will not be in the main desktop bundle, but will be in the web bundle. +// In desktop, we will bundle/copye each of these separately into the node_modules folder. +const deskTopNodeModulesToExternalize = [ + 'pdfkit/js/pdfkit.standalone', + 'crypto-js', + 'fontkit', + 'png-js', + 'zeromq', // Copy, do not bundle + 'zeromqold', // Copy, do not bundle + // Its lazy loaded by Jupyter lab code, & since this isn't used directly in our code + // there's no need to include into the main bundle. + 'node-fetch', + // Its loaded by node-fetch, & since that is lazy loaded + // there's no need to include into the main bundle. + 'iconv-lite', + // Its loaded by ivonv-lite, & since that is lazy loaded + // there's no need to include into the main bundle. + 'fontkit', + 'svg-to-pdfkit', + // Lazy loaded modules. + 'vscode-languageclient/node', + '@vscode/jupyter-lsp-middleware', + '@vscode/extension-telemetry', + '@vscode/lsp-notebook-concat', + '@jupyterlab/services', + '@jupyterlab/nbformat', + '@jupyterlab/services/lib/kernel/serialize', + '@jupyterlab/services/lib/kernel/nonSerializingKernel', + 'vscode-jsonrpc' // Used by a few modules, might as well pull this out, instead of duplicating it in separate bundles. +]; +const commonExternals = [ + 'log4js', + 'vscode', + 'commonjs', + 'node:crypto', + 'vscode-jsonrpc', // Used by a few modules, might as well pull this out, instead of duplicating it in separate bundles. + // jsonc-parser doesn't get bundled well with esbuild without any changes. + // Its possible the fix is very simple. + // For now, its handled with webpack. + 'jsonc-parser', + // Ignore telemetry specific packages that are not required. + 'applicationinsights-native-metrics', + '@opentelemetry/tracing', + '@azure/opentelemetry-instrumentation-azure-sdk', + '@opentelemetry/instrumentation', + '@azure/functions-core' +]; +const webExternals = commonExternals.concat('os').concat(commonExternals); +const desktopExternals = commonExternals.concat(deskTopNodeModulesToExternalize); +const bundleConfig = getBundleConfiguration(); const isDevbuild = !process.argv.includes('--production'); const isWatchMode = process.argv.includes('--watch'); const extensionFolder = path.join(__dirname, '..', '..'); +if (!isDevbuild) { + const packageJson = JSON.parse(fs.readFileSync(path.join(extensionFolder, 'package.json')).toString()); + if (packageJson.main.includes('out')) { + throw new Error('`package.json` should not contain `out` in the `main` property'); + } + if (packageJson.browser.includes('out')) { + throw new Error('`package.json` should not contain `out` in the `browser` property'); + } +} + interface StylePluginOptions { /** * whether to minify the css code. @@ -104,16 +164,31 @@ function createConfig( outfile: string, target: 'desktop' | 'web' ): SameShape { - const inject = [path.join(__dirname, isDevbuild ? 'process.development.js' : 'process.production.js')]; + const inject: string[] = []; + if (target === 'web') { + inject.push(path.join(__dirname, isDevbuild ? 'process.development.js' : 'process.production.js')); + } if (source.endsWith(path.join('data-explorer', 'index.tsx'))) { inject.push(path.join(__dirname, 'jquery.js')); } + const external = target === 'web' ? webExternals : commonExternals; + if (source.toLowerCase().endsWith('extension.node.ts')) { + external.push(...desktopExternals); + } + 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 = { + moment: path.join(extensionFolder, 'build', 'webpack', 'moment.js'), + 'vscode-jupyter-relese-version': path.join(__dirname, releaseVersionScriptFile) + }; return { entryPoints: [source], outfile, bundle: true, - external: ['log4js', 'vscode', 'commonjs', 'node:crypto'].concat(target === 'web' ? ['os'] : []), // From webacpk scripts we had. - format: 'esm', + external, + alias, + format: target === 'desktop' ? 'cjs' : 'esm', + metafile: isDevbuild && !isWatchMode, define: target === 'desktop' ? undefined @@ -140,13 +215,17 @@ async function build( const context = await esbuild.context(createConfig(source, outfile, options.target)); await context.watch(); } else { - await esbuild.build(createConfig(source, outfile, options.target)); + const result = await esbuild.build(createConfig(source, outfile, options.target)); const size = fs.statSync(outfile).size; const relativePath = `./${path.relative(extensionFolder, outfile)}`; console.log(`asset ${green(relativePath)} size: ${(size / 1024).toFixed()} KiB`); + if (isDevbuild && result.metafile) { + const metafile = `${outfile}.esbuild.meta.json`; + await fs.writeFile(metafile, JSON.stringify(result.metafile)); + console.log(`metafile ${green(`./${path.relative(extensionFolder, metafile)}`)}`); + } } } -async function watch(source: string, outfile: string) {} async function buildAll() { // First build the less file format, convert to css, and then build tsx to use the css @@ -169,13 +248,11 @@ async function buildAll() { 'webview-side', 'interactive-common', 'variableExplorerGrid.css' - ), - watch + ) ), build( path.join(extensionFolder, 'src', 'webviews', 'webview-side', 'react-common', 'seti', 'seti.less'), - path.join(extensionFolder, 'src', 'webviews', 'webview-side', 'react-common', 'seti', 'seti.css'), - watch + path.join(extensionFolder, 'src', 'webviews', 'webview-side', 'react-common', 'seti', 'seti.css') ) ]; }; @@ -186,49 +263,186 @@ async function buildAll() { ...[isWatchMode ? getLessBuilders(true) : []], build( path.join(extensionFolder, 'src', 'webviews', 'webview-side', 'ipywidgets', 'kernel', 'index.ts'), - path.join(extensionFolder, 'out', 'webviews', 'webview-side', 'ipywidgetsKernel', 'ipywidgetsKernel.js') + path.join(extensionFolder, 'dist', 'webviews', 'webview-side', 'ipywidgetsKernel', 'ipywidgetsKernel.js') ), build( path.join(extensionFolder, 'src', 'webviews', 'webview-side', 'ipywidgets', 'renderer', 'index.ts'), - path.join(extensionFolder, 'out', 'webviews', 'webview-side', 'ipywidgetsRenderer', 'ipywidgetsRenderer.js') + path.join( + extensionFolder, + 'dist', + 'webviews', + 'webview-side', + 'ipywidgetsRenderer', + 'ipywidgetsRenderer.js' + ) ), build( path.join(extensionFolder, 'src', 'webviews', 'webview-side', 'variable-view', 'index.tsx'), - path.join(extensionFolder, 'out', 'webviews', 'webview-side', 'viewers', 'variableView.js') + path.join(extensionFolder, 'dist', 'webviews', 'webview-side', 'viewers', 'variableView.js') ), build( path.join(extensionFolder, 'src', 'webviews', 'webview-side', 'plot', 'index.tsx'), - path.join(extensionFolder, 'out', 'webviews', 'webview-side', 'viewers', 'plotViewer.js') + path.join(extensionFolder, 'dist', 'webviews', 'webview-side', 'viewers', 'plotViewer.js') ), build( path.join(extensionFolder, 'src', 'webviews', 'webview-side', 'data-explorer', 'index.tsx'), - path.join(extensionFolder, 'out', 'webviews', 'webview-side', 'viewers', 'dataExplorer.js') + path.join(extensionFolder, 'dist', 'webviews', 'webview-side', 'viewers', 'dataExplorer.js') ), , isDevbuild ? build( path.join(extensionFolder, 'src', 'test', 'datascience', 'widgets', 'rendererUtils.ts'), - path.join(extensionFolder, 'out', 'webviews', 'webview-side', 'widgetTester', 'widgetTester.js') + path.join(extensionFolder, 'dist', 'webviews', 'webview-side', 'widgetTester', 'widgetTester.js') ) : Promise.resolve(), , - build( - path.join(extensionFolder, 'src', 'extension.web.ts'), - path.join(extensionFolder, 'out', 'extension.web.js') - ), - ...nodeModulesToExternalize.map(async (module) => { - let fullPath = path.join(extensionFolder, 'node_modules', `${module}.js`); - if (!fs.existsSync(fullPath)) { - fullPath = require.resolve(path.join(extensionFolder, 'node_modules', module)); - console.error(fullPath); - } - return build(fullPath, fullPath.replace('node_modules', path.join('out', 'node_modules')), { - target: 'desktop', - watch: isWatchMode - }); - }) + bundleConfig === 'desktop' + ? Promise.resolve() + : build( + path.join(extensionFolder, 'src', 'extension.web.ts'), + path.join(extensionFolder, 'dist', 'extension.web.bundle.js') + ), + bundleConfig === 'web' + ? Promise.resolve() + : build( + path.join(extensionFolder, 'src', 'extension.node.ts'), + path.join(extensionFolder, 'dist', 'extension.node.js'), + { target: 'desktop', watch: isWatchMode } + ), + bundleConfig === 'web' + ? Promise.resolve() + : build( + path.join(extensionFolder, 'src', 'extension.node.proxy.ts'), + path.join(extensionFolder, 'dist', 'extension.node.proxy.js'), + { target: 'desktop', watch: isWatchMode } + ), + ...(bundleConfig === 'web' ? [] : deskTopNodeModulesToExternalize) + // zeromq will be manually bundled. + .filter((module) => !['zeromq', 'zeromqold', 'vscode-jsonrpc'].includes(module)) + + .map(async (module) => { + const fullPath = require.resolve(module); + return build(fullPath, path.join(extensionFolder, 'dist', 'node_modules', `${module}.js`), { + target: 'desktop', + watch: isWatchMode + }); + }), + ...(bundleConfig === 'web' + ? [] + : [copyJQuery(), copyAminya(), copyZeroMQ(), copyZeroMQOld(), buildVSCodeJsonRPC()]) + ]); +} + +/** + * TODO: Who uses JQuery? + * Possibly shipped for widgets. + * Need to verify this, if this is the case, then possibly best shipped with renderers. + */ +async function copyJQuery() { + const source = require.resolve('jquery').replace('jquery.js', 'jquery.min.js'); + const target = path.join(extensionFolder, 'dist', 'node_modules', 'jquery', 'out', 'jquery.min.js'); + const license = require.resolve('jquery').replace(path.join('out', 'jquery.js'), 'LICENSE.txt'); + await fs.ensureDir(path.dirname(target)); + await Promise.all([ + fs.copyFile(source, target), + fs.copyFile(license, path.join(extensionFolder, 'dist', 'node_modules', 'jquery', 'LICENSE.txt')) ]); } +async function copyAminya() { + const source = path.join(extensionFolder, 'node_modules', '@aminya/node-gyp-build'); + const target = path.join(extensionFolder, 'dist', 'node_modules', '@aminya/node-gyp-build'); + await fs.ensureDir(path.dirname(target)); + await fs.ensureDir(target); + await fs.copy(source, target, { recursive: true }); +} +async function copyZeroMQ() { + const source = path.join(extensionFolder, 'node_modules', 'zeromq'); + const target = path.join(extensionFolder, 'dist', 'node_modules', 'zeromq'); + await fs.ensureDir(target); + await fs.copy(source, target, { + recursive: true, + filter: (src) => shouldCopyFileFromZmqFolder(src) + }); +} +async function copyZeroMQOld() { + const source = path.join(extensionFolder, 'node_modules', 'zeromqold'); + const target = path.join(extensionFolder, 'dist', 'node_modules', 'zeromqold'); + await fs.ensureDir(path.dirname(target)); + await fs.ensureDir(target); + await fs.copy(source, target, { + recursive: true, + filter: (src) => shouldCopyFileFromZmqFolder(src) + }); +} +async function buildVSCodeJsonRPC() { + const source = path.join(extensionFolder, 'node_modules', 'vscode-jsonrpc'); + const target = path.join(extensionFolder, 'dist', 'node_modules', 'vscode-jsonrpc', 'index.js'); + await fs.ensureDir(path.dirname(target)); + const fullPath = require.resolve(source); + const contents = ` +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * ----------------------------------------------------------------------------------------- */ +'use strict'; + +module.exports = require('./index');`; + await fs.writeFile(path.join(path.dirname(target), 'node.js'), contents); + return build(fullPath, target, { + target: 'desktop', + watch: isWatchMode + }); +} + +function shouldCopyFileFromZmqFolder(resourcePath) { + const parentFolder = path.dirname(resourcePath); + if (fs.statSync(resourcePath).isDirectory()) { + return true; + } + // return true; + const filename = path.basename(resourcePath); + // Ensure the code is platform agnostic. + resourcePath = (resourcePath || '').toString().toLowerCase().replace(/\\/g, '/'); + // We do not need to bundle these folders + const foldersToIgnore = ['build', 'script', 'src', 'node_modules', 'vendor']; + if ( + foldersToIgnore.some((folder) => + resourcePath.toLowerCase().startsWith(path.join(parentFolder, folder).replace(/\\/g, '/').toLowerCase()) + ) + ) { + return false; + } + + if ( + resourcePath.endsWith('.js') || + resourcePath.endsWith('.json') || + resourcePath.endsWith('.md') || + resourcePath.endsWith('license') + ) { + return true; + } + // if (!resourcePath.includes(path.join(parentFolder, 'prebuilds').replace(/\\/g, '/').toLowerCase())) { + if (!parentFolder.includes(`${path.sep}prebuilds${path.sep}`)) { + // We do not ship any other sub directory. + return false; + } + if (filename.includes('electron.') && resourcePath.endsWith('.node')) { + // We do not ship electron binaries. + return false; + } + const preBuildsFoldersToCopy = getZeroMQPreBuildsFoldersToKeep(); + if (preBuildsFoldersToCopy.length === 0) { + // Copy everything from all prebuilds folder. + return true; + } + // Copy if this is a prebuilds folder that needs to be copied across. + // Use path.sep as the delimiter, as we do not want linux-arm64 to get compiled with search criteria is linux-arm. + if (preBuildsFoldersToCopy.some((folder) => resourcePath.includes(`${folder.toLowerCase()}/`))) { + return true; + } + return false; +} + const started = Date.now(); buildAll(); diff --git a/build/esbuild/build.widgets.ts b/build/esbuild/build.widgets.ts deleted file mode 100644 index 72a5e7ef735..00000000000 --- a/build/esbuild/build.widgets.ts +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import * as path from 'path'; -import * as esbuild from 'esbuild'; -import { green } from 'colors'; -import type { BuildOptions, Charset, Plugin, SameShape } from 'esbuild'; -import fs from 'fs'; - -const isDevbuild = !process.argv.includes('--production'); -const isWatchMode = process.argv.includes('--watch'); -const extensionFolder = path.join(__dirname, '..', '..'); - -interface StylePluginOptions { - /** - * whether to minify the css code. - * @default true - */ - minify?: boolean; - - /** - * css charset. - * @default 'utf8' - */ - charset?: Charset; -} - -// https://github.com/evanw/esbuild/issues/20#issuecomment-802269745 -// https://github.com/hyrious/esbuild-plugin-style -function style({ minify = true, charset = 'utf8' }: StylePluginOptions = {}): Plugin { - return { - name: 'style', - setup({ onResolve, onLoad }) { - const cwd = process.cwd(); - const opt: BuildOptions = { logLevel: 'silent', bundle: true, write: false, charset, minify }; - - onResolve({ filter: /\.css$/, namespace: 'file' }, (args) => { - const absPath = path.join(args.resolveDir, args.path); - const relPath = path.relative(cwd, absPath); - const resolved = fs.existsSync(absPath) ? relPath : args.path; - return { path: resolved, namespace: 'style-stub' }; - }); - - onResolve({ filter: /\.css$/, namespace: 'style-stub' }, (args) => { - return { path: args.path, namespace: 'style-content' }; - }); - - onResolve({ filter: /^__style_helper__$/, namespace: 'style-stub' }, (args) => ({ - path: args.path, - namespace: 'style-helper', - sideEffects: false - })); - - onLoad({ filter: /.*/, namespace: 'style-helper' }, async () => ({ - contents: ` - export function injectStyle(text) { - if (typeof document !== 'undefined') { - var style = document.createElement('style') - var node = document.createTextNode(text) - style.appendChild(node) - document.head.appendChild(style) - } - } - ` - })); - - onLoad({ filter: /.*/, namespace: 'style-stub' }, async (args) => ({ - contents: ` - import { injectStyle } from "__style_helper__" - import css from ${JSON.stringify(args.path)} - injectStyle(css) - ` - })); - - onLoad({ filter: /.*/, namespace: 'style-content' }, async (args) => { - const options = { entryPoints: [args.path], ...opt }; - const { errors, warnings, outputFiles } = await esbuild.build(options); - return { errors, warnings, contents: outputFiles![0].text, loader: 'text' }; - }); - } - }; -} - -function createConfig(source: string, outfile: string): SameShape { - return { - entryPoints: [source], - outfile, - bundle: true, - external: ['log4js'], // From webacpk scripts we had. - format: 'esm', - define: { - BROWSER: 'true', // From webacpk scripts we had. - global: 'this' - }, - target: 'es2018', - minify: !isDevbuild, - logLevel: 'info', - sourcemap: isDevbuild, - inject: [path.join(__dirname, isDevbuild ? 'process.development.js' : 'process.production.js')], - plugins: [style()] - }; -} -async function build(source: string, outfile: string) { - if (isWatchMode) { - const context = await esbuild.context(createConfig(source, outfile)); - await context.watch(); - } else { - await esbuild.build(createConfig(source, outfile)); - const size = fs.statSync(outfile).size; - const relativePath = `./${path.relative(extensionFolder, outfile)}`; - console.log(`asset ${green(relativePath)} size: ${(size / 1024).toFixed()} KiB`); - } -} -async function watch(source: string, outfile: string) {} - -async function buildAll() { - await Promise.all([ - build( - path.join(extensionFolder, 'src', 'webviews', 'webview-side', 'ipywidgets', 'kernel', 'index.ts'), - path.join(extensionFolder, 'out', 'webviews', 'webview-side', 'ipywidgetsKernel', 'ipywidgetsKernel.js') - ), - build( - path.join(extensionFolder, 'src', 'webviews', 'webview-side', 'ipywidgets', 'renderer', 'index.ts'), - path.join(extensionFolder, 'out', 'webviews', 'webview-side', 'ipywidgetsRenderer', 'ipywidgetsRenderer.js') - ), - isDevbuild - ? build( - path.join(extensionFolder, 'src', 'test', 'datascience', 'widgets', 'rendererUtils.ts'), - path.join(extensionFolder, 'out', 'webviews', 'webview-side', 'widgetTester', 'widgetTester.js') - ) - : Promise.resolve() - ]); -} - -const started = Date.now(); -buildAll().then(() => console.log(`Watching for changes in esbuild...`)); diff --git a/build/esbuild/release.pre-release.js b/build/esbuild/release.pre-release.js new file mode 100644 index 00000000000..b76a981a45e --- /dev/null +++ b/build/esbuild/release.pre-release.js @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +export const isPreRelesVersionOfJupyterExtension = true; diff --git a/build/esbuild/release.stable.js b/build/esbuild/release.stable.js new file mode 100644 index 00000000000..8f22b35678d --- /dev/null +++ b/build/esbuild/release.stable.js @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +export const isPreRelesVersionOfJupyterExtension = false; diff --git a/build/launchWebUtils.js b/build/launchWebUtils.js index 1db5cedbc00..c8ada2675d1 100644 --- a/build/launchWebUtils.js +++ b/build/launchWebUtils.js @@ -27,19 +27,34 @@ exports.launch = async function launch(launchTests) { server = (await startJupyter()).server; testServer = await startReportServer(); } - const bundlePath = path.join(extensionDevelopmentPath, 'out', 'extension.web.bundle'); + const bundlePath = path.join(extensionDevelopmentPath, launchTests ? 'out' : 'dist', 'extension.web.bundle'); // Changing the logging level to be read from workspace settings file. // This way we can enable verbose logging and get the logs for web tests. - const settingsJson = fs.readFileSync(packageJsonFile).toString(); - const edits = jsonc.modify( + // Changing the logging level to be read from workspace settings file. + // This way we can enable verbose logging and get the logs for web tests. + let settingsJson = fs.readFileSync(packageJsonFile).toString(); + settingsJson = jsonc.applyEdits( settingsJson, - ['contributes', 'configuration', 'properties', 'jupyter.logging.level', 'scope'], - 'resource', - {} + jsonc.modify( + settingsJson, + ['contributes', 'configuration', 'properties', 'jupyter.logging.level', 'scope'], + 'resource', + {} + ) ); - const updatedSettingsJson = jsonc.applyEdits(settingsJson, edits); - fs.writeFileSync(packageJsonFile, updatedSettingsJson); + // Tests scripts are in the 'out' folder. + if (launchTests) { + settingsJson = jsonc.applyEdits( + settingsJson, + jsonc.modify(settingsJson, ['main'], './out/extension.node.js', {}) + ); + settingsJson = jsonc.applyEdits( + settingsJson, + jsonc.modify(settingsJson, ['browser'], './out/extension.web.bundle.js', {}) + ); + } + fs.writeFileSync(packageJsonFile, settingsJson); const options = { browserType, verbose: true, diff --git a/build/webpack/webpack.extension.dependencies.config.js b/build/webpack/webpack.extension.dependencies.config.js new file mode 100644 index 00000000000..748f4d0a7aa --- /dev/null +++ b/build/webpack/webpack.extension.dependencies.config.js @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +'use strict'; + +const path = require('path'); +const constants = require('../constants'); +const common = require('./common'); +const entryItems = { + // jsonc-parser does not get bundled propertly with esbuild. + [`node_modules/jsonc-parser`]: `./node_modules/jsonc-parser` +}; + +const config = { + mode: 'production', + target: 'node', + context: constants.ExtensionRootDir, + entry: entryItems, + devtool: 'source-map', + node: { + __dirname: false + }, + externals: ['vscode', 'commonjs', 'vscode-jsonrpc'], + plugins: [...common.getDefaultPlugins('dependencies')], + resolve: { + extensions: ['.js'] + }, + output: { + filename: '[name].js', + path: path.resolve(constants.ExtensionRootDir, 'dist'), + libraryTarget: 'commonjs2', + devtoolModuleFilenameTemplate: '../../[resource-path]' + } +}; +// tslint:disable-next-line:no-default-export +exports.default = config; diff --git a/gulpfile.js b/gulpfile.js index 6e56ca0193c..387bc9229b7 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -27,19 +27,6 @@ const { stripVTControlCharacters } = require('util'); const common = require('./build/webpack/common'); const jsonc = require('jsonc-parser'); -gulp.task('compile', async (done) => { - // Use tsc so we can generate source maps that look just like tsc does (gulp-sourcemap does not generate them the same way) - try { - const stdout = await spawnAsync('tsc', ['-p', './'], {}, true); - if (stdout.toLowerCase().includes('error ts')) { - throw new Error(`Compile errors: \n${stdout}`); - } - done(); - } catch (e) { - done(e); - } -}); - gulp.task('createNycFolder', async (done) => { try { const fs = require('fs'); @@ -104,9 +91,9 @@ gulp.task('printTestResults', async (done) => { gulp.task('output:clean', () => del(['coverage'])); -gulp.task('clean:cleanExceptTests', () => del(['clean:vsix', 'out', '!out/test'])); +gulp.task('clean:cleanExceptTests', () => del(['clean:vsix', 'out', 'dist', '!out/test'])); gulp.task('clean:vsix', () => del(['*.vsix'])); -gulp.task('clean:out', () => del(['out/**', '!out', '!out/client_renderer/**', '!**/*nls.*.json'])); +gulp.task('clean:out', () => del(['out/**', 'dist/**', '!out', '!out/client_renderer/**', '!**/*nls.*.json'])); gulp.task('clean', gulp.parallel('output:clean', 'clean:vsix', 'clean:out')); @@ -189,6 +176,13 @@ async function buildWebPackForDevOrProduction(configFile, configNameForProductio await spawnAsync('npm', ['run', 'webpack', '--', '--config', configFile, '--mode', 'development'], webpackEnv); } } +gulp.task('webpack-dependencies', async () => { + // No need to build dependencies for web. + if (common.getBundleConfiguration() === common.bundleConfiguration.web) { + return; + } + await buildWebPackForDevOrProduction('./build/webpack/webpack.extension.dependencies.config.js', 'production'); +}); gulp.task('webpack-extension-node', async () => { // No need to build dependencies for web. @@ -226,22 +220,22 @@ gulp.task('updatePackageJsonForBundle', async () => { modifyJson(packageJsonFile, () => ['browser', undefined]); } if (!json.main) { - modifyJson(packageJsonFile, () => ['main', './out/extension.node.js']); + modifyJson(packageJsonFile, () => ['main', './dist/extension.node.js']); } break; } case common.bundleConfiguration.webAndDesktop: { if (!json.browser) { - modifyJson(packageJsonFile, () => ['browser', './out/extension.web.bundle.js']); + modifyJson(packageJsonFile, () => ['browser', './dist/extension.web.bundle.js']); } if (!json.main) { - modifyJson(packageJsonFile, () => ['main', './out/extension.node.js']); + modifyJson(packageJsonFile, () => ['main', './dist/extension.node.js']); } break; } case common.bundleConfiguration.web: { if (!json.browser) { - modifyJson(packageJsonFile, () => ['browser', './out/extension.web.bundle.js']); + modifyJson(packageJsonFile, () => ['browser', './dist/extension.web.bundle.js']); } if (json.main) { modifyJson(packageJsonFile, () => ['main', undefined]); @@ -365,7 +359,7 @@ function getAllowedWarningsForWebPack(buildConfig) { 'WARNING in ./node_modules/log4js/lib/appenders/index.js', 'WARNING in ./node_modules/log4js/lib/clustering.js', 'WARNING in ./node_modules/diagnostic-channel-publishers/dist/src/azure-coretracing.pub.js', - 'WARNING in ./node_modules/applicationinsights/out/AutoCollection/NativePerformance.js' + 'WARNING in ./node_modules/applicationinsights/dist/AutoCollection/NativePerformance.js' ]; case 'extension': return [ @@ -385,40 +379,32 @@ function getAllowedWarningsForWebPack(buildConfig) { 'WARNING in ./node_modules/@jupyterlab/services/node_modules/ws/lib/validation.js', 'WARNING in ./node_modules/@jupyterlab/services/node_modules/ws/lib/Validation.js', 'WARNING in ./node_modules/diagnostic-channel-publishers/dist/src/azure-coretracing.pub.js', - 'WARNING in ./node_modules/applicationinsights/out/AutoCollection/NativePerformance.js' + 'WARNING in ./node_modules/applicationinsights/dist/AutoCollection/NativePerformance.js' ]; case 'debugAdapter': return [ 'WARNING in ./node_modules/vscode-uri/lib/index.js', 'WARNING in ./node_modules/diagnostic-channel-publishers/dist/src/azure-coretracing.pub.js', - 'WARNING in ./node_modules/applicationinsights/out/AutoCollection/NativePerformance.js' + 'WARNING in ./node_modules/applicationinsights/dist/AutoCollection/NativePerformance.js' ]; default: throw new Error('Unknown WebPack Configuration'); } } -gulp.task('compile-webviews-release', async () => { - await spawnAsync('npm', ['run', 'compile-viewers-release'], webpackEnv); +gulp.task('compile-release', async () => { + await spawnAsync('npm', ['run', 'compile-release'], webpackEnv); }); -gulp.task('compile-webviews-dev', async () => { - await spawnAsync('npm', ['run', 'compile-viewers'], webpackEnv); +gulp.task('prePublishBundle', async () => { + await spawnAsync('npm', ['run', 'prePublishBundle'], webpackEnv); }); -gulp.task( - 'prePublishBundle', - gulp.series( - // Dependencies first - 'compile-webviews-release', - // Then the two extensions - gulp.parallel('webpack-extension-node', 'webpack-extension-web'), - 'updatePackageJsonForBundle' - ) -); gulp.task('checkDependencies', gulp.series('checkNativeDependencies', 'checkNpmDependencies')); -gulp.task('prePublishNonBundle', gulp.parallel('compile', 'compile-webviews-dev')); +gulp.task('prePublishNonBundle', async () => { + await spawnAsync('npm', ['run', 'prePublishNonBundle'], webpackEnv); +}); function spawnAsync(command, args, env, rejectOnStdErr = false) { env = env || {}; diff --git a/package-lock.json b/package-lock.json index 7bced48774f..7a59daa251f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -159,13 +159,12 @@ "clean-terminal-webpack-plugin": "^3.0.0", "codecov": "^3.7.1", "colors": "^1.4.0", - "concurrently": "^8.2.1", + "concurrently": "^8.2.2", "copy-webpack-plugin": "^11.0.0", "cross-env": "^7.0.3", "cross-spawn": "^7.0.3", "css-loader": "^6.8.1", "dedent": "^0.7.0", - "deemon": "^1.4.0", "del": "^3.0.0", "es-abstract": "^1.19.1", "es5-ext": "^0.10.53", @@ -7072,9 +7071,9 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "node_modules/concurrently": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.1.tgz", - "integrity": "sha512-nVraf3aXOpIcNud5pB9M82p1tynmZkrSGQ1p6X/VY8cJ+2LMVqAgXsJxYYefACSHbTYlm92O1xuhdGTjwoEvbQ==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", + "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", "dev": true, "dependencies": { "chalk": "^4.1.2", @@ -7856,22 +7855,6 @@ "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", "dev": true }, - "node_modules/deemon": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/deemon/-/deemon-1.7.0.tgz", - "integrity": "sha512-v6wnuuvN8cXNcfYaQ4EsYIYMmBKpEdhcgWzBVhYR0zrXg/LebF6kWSVNvjc3v5IPTFZA1ReHrP2MrOa0Zi9kIA==", - "dev": true, - "dependencies": { - "bl": "^4.0.2", - "tree-kill": "^1.2.2" - }, - "bin": { - "deemon": "src/deemon.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/deep-diff": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz", @@ -29122,9 +29105,9 @@ } }, "concurrently": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.1.tgz", - "integrity": "sha512-nVraf3aXOpIcNud5pB9M82p1tynmZkrSGQ1p6X/VY8cJ+2LMVqAgXsJxYYefACSHbTYlm92O1xuhdGTjwoEvbQ==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", + "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", "dev": true, "requires": { "chalk": "^4.1.2", @@ -29712,16 +29695,6 @@ "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", "dev": true }, - "deemon": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/deemon/-/deemon-1.7.0.tgz", - "integrity": "sha512-v6wnuuvN8cXNcfYaQ4EsYIYMmBKpEdhcgWzBVhYR0zrXg/LebF6kWSVNvjc3v5IPTFZA1ReHrP2MrOa0Zi9kIA==", - "dev": true, - "requires": { - "bl": "^4.0.2", - "tree-kill": "^1.2.2" - } - }, "deep-diff": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz", diff --git a/package.json b/package.json index e6ea973cb12..b69c2efb3f3 100644 --- a/package.json +++ b/package.json @@ -58,8 +58,8 @@ "onWebviewPanel:jupyter-variables", "onWebviewPanel:jupyter" ], - "main": "./out/extension.node.js", - "browser": "./out/extension.web.bundle.js", + "main": "./dist/extension.node.proxy.js", + "browser": "./dist/extension.web.bundle.js", "capabilities": { "virtualWorkspaces": true, "untrustedWorkspaces": { @@ -1977,20 +1977,20 @@ "localResourceRoots": [ "./temp" ], - "entrypoint": "./out/webviews/webview-side/ipywidgetsKernel/ipywidgetsKernel.js" + "entrypoint": "./dist/webviews/webview-side/ipywidgetsKernel/ipywidgetsKernel.js" }, { "type": "interactive", "localResourceRoots": [ "./temp" ], - "entrypoint": "./out/webviews/webview-side/ipywidgetsKernel/ipywidgetsKernel.js" + "entrypoint": "./dist/webviews/webview-side/ipywidgetsKernel/ipywidgetsKernel.js" } ], "notebookRenderer": [ { "id": "jupyter-ipywidget-renderer", - "entrypoint": "./out/webviews/webview-side/ipywidgetsRenderer/ipywidgetsRenderer.js", + "entrypoint": "./dist/webviews/webview-side/ipywidgetsRenderer/ipywidgetsRenderer.js", "displayName": "%jupyter.notebookRenderer.IPyWidget.displayName%", "mimeTypes": [ "application/vnd.jupyter.widget-view+json" @@ -2070,19 +2070,23 @@ "packagePreRelease": "gulp clean && gulp prePublishBundle && vsce package --pre-release -o ms-toolsai-jupyter-insiders.vsix", "prePublishBundleStable": "cross-env IS_PRE_RELEASE_VERSION_OF_JUPYTER_EXTENSION=false gulp prePublishBundle", "prePublishBundlePreRelease": "cross-env IS_PRE_RELEASE_VERSION_OF_JUPYTER_EXTENSION=true gulp prePublishBundle", - "prePublishNonBundle": "gulp clean && gulp prePublishNonBundle", "createNycFolder": "gulp createNycFolder", "validateTranslationFiles": "gulp validateTranslationFiles", - "compile": "tsc -watch -p ./", - "compile-no-watch": "tsc -p ./", - "compiled": "deemon npm run compile", - "kill-compiled": "deemon --kill npm run compile", - "compile-web": "npm run compile-viewers", + "preprePublishBundle": "gulp clean", + "prePublishBundle": "concurrently npm:compile-release 'npx gulp updatePackageJsonForBundle'", + "preprePublishNonBundle": "gulp clean", + "prePublishNonBundle": "npm run compile-no-watch", + "compile": "concurrently 'tsc -watch -p ./' npm:webpack-dev 'npx tsx build/esbuild/build.ts --watch'", + "compile-no-watch": "concurrently npm:compile-out npm:webpack-dev npm:esbuild-all", + "compile-out": "tsc -p ./", "compile-web-test": "cross-env VSC_TEST_BUNDLE=true webpack --mode development --config ./build/webpack/webpack.extension.web.config.js", "compile-web-test-watch": "cross-env VSC_TEST_BUNDLE=true webpack --mode development --config ./build/webpack/webpack.extension.web.config.js --stats-error-details --watch --progress", - "compile-viewers": "npx tsx build/esbuild/build.ts", - "compile-viewers-release": "npx tsx build/esbuild/build.ts --production", - "compile-viewers-watch": "npx tsx build/esbuild/build.ts --watch", + "esbuild-all": "npx tsx build/esbuild/build.ts", + "compile-release": "concurrently npm:compile-out npm:esbuild-release npm:webpack-release", + "esbuild-release": "npx tsx build/esbuild/build.ts --production", + "webpack-dev": "gulp webpack-dependencies", + "webpack-release": "gulp webpack-dependencies", + "esbuild-watch": "npx tsx build/esbuild/build.ts --watch", "checkDependencies": "gulp checkDependencies", "postinstall": "npm run download-api && node ./build/ci/postInstall.js", "installPythonLibs": "gulp installPythonLibs", @@ -2114,9 +2118,7 @@ "validateTelemetry": "gulp validateTelemetry", "verifyUnhandledErrors": "gulp verifyUnhandledErrors", "printTestResults": "gulp printTestResults", - "setup-precommit-hook": "husky install", - "webpack-analyze:node": "npx webpack --json --config build/webpack/webpack.extension.node.config.js > out/stats-node.json && yarn webpack-bundle-analyzer out/stats-node.json", - "webpack-analyze:web": "npx webpack --json --config build/webpack/webpack.extension.web.config.js > out/stats-web.json && yarn webpack-bundle-analyzer out/stats-web.json" + "setup-precommit-hook": "husky install" }, "dependencies": { "@c4312/evt": "^0.1.1", @@ -2268,13 +2270,12 @@ "clean-terminal-webpack-plugin": "^3.0.0", "codecov": "^3.7.1", "colors": "^1.4.0", - "concurrently": "^8.2.1", + "concurrently": "^8.2.2", "copy-webpack-plugin": "^11.0.0", "cross-env": "^7.0.3", "cross-spawn": "^7.0.3", "css-loader": "^6.8.1", "dedent": "^0.7.0", - "deemon": "^1.4.0", "del": "^3.0.0", "es-abstract": "^1.19.1", "es5-ext": "^0.10.53", diff --git a/pvsc.code-workspace b/pvsc.code-workspace index b642f55e15e..a022ae39227 100644 --- a/pvsc.code-workspace +++ b/pvsc.code-workspace @@ -31,7 +31,8 @@ "**/.vscode test/stable/**": true, "**/.vscode-test/insider/**": true, "**/.vscode-test/stable/**": true, - "**/out/**": true + "**/out/**": true, + "**/dist/**": true } }, "launch": { @@ -53,7 +54,8 @@ "${workspaceFolder:vscode-python}/out/**/*.js", "!${workspaceFolder:vscode-python}/**/node_modules**/*", "${workspaceFolder:vscode-jupyter}/out/**/*.js", - "!${workspaceFolder:vscode-jupyter}/**/node_modules**/*" + "${workspaceFolder:vscode-jupyter}/dist/**/*.js", + "!${workspaceFolder:vscode-jupyter}/**/node_modules**/*", ], "skipFiles": ["/**"] }, @@ -78,6 +80,7 @@ "${workspaceFolder:vscode-python}/out/**/*.js", "!${workspaceFolder:vscode-python}/**/node_modules**/*", "${workspaceFolder:vscode-jupyter}/out/**/*.js", + "${workspaceFolder:vscode-jupyter}/dist/**/*.js", "!${workspaceFolder:vscode-jupyter}/**/node_modules**/*" ], "skipFiles": ["/**"] @@ -97,6 +100,7 @@ "${workspaceFolder:vscode-python}/out/**/*.js", "!${workspaceFolder:vscode-python}/**/node_modules**/*", "${workspaceFolder:vscode-jupyter}/out/**/*.js", + "${workspaceFolder:vscode-jupyter}/dist/**/*.js", "!${workspaceFolder:vscode-jupyter}/**/node_modules**/*", "${workspaceFolder:vscode-notebook-renderers}/out/**/*.js", "!${workspaceFolder:vscode-notebook-renderers}/**/node_modules**/*" @@ -115,6 +119,7 @@ "sourceMaps": true, "outFiles": [ "${workspaceFolder:vscode-jupyter}/out/**/*.js", + "${workspaceFolder:vscode-jupyter}/dist/**/*.js", "!${workspaceFolder:vscode-jupyter}/**/node_modules**/*", "${workspaceFolder:vscode-jupyter-powertoys}/out/**/*.js", "!${workspaceFolder:vscode-jupyter-powertoys}/**/node_modules**/*" @@ -133,6 +138,7 @@ "sourceMaps": true, "outFiles": [ "${workspaceFolder:vscode-jupyter}/out/**/*.js", + "${workspaceFolder:vscode-jupyter}/dist/**/*.js", "!${workspaceFolder:vscode-jupyter}/**/node_modules**/*", "${workspaceFolder:vscode-notebook-renderers}/out/**/*.js", "!${workspaceFolder:vscode-notebook-renderers}/**/node_modules**/*" @@ -164,6 +170,7 @@ "sourceMaps": true, "outFiles": [ "${workspaceFolder:vscode-jupyter}/out/**/*.js", + "${workspaceFolder:vscode-jupyter}/dist/**/*.js", "!${workspaceFolder:vscode-jupyter}/**/node_modules**/*" ], "skipFiles": ["/**"] diff --git a/src/extension.node.proxy.ts b/src/extension.node.proxy.ts new file mode 100644 index 00000000000..a40a493d480 --- /dev/null +++ b/src/extension.node.proxy.ts @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import type { IExtensionApi } from './standalone/api/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 { + const entryPoint = context.extensionMode === ExtensionMode.Test ? '../out/extension.node' : './extension.node'; + try { + realEntryPoint = eval('require')(entryPoint); + return realEntryPoint.activate(context); + } catch (ex) { + console.error('Failed to activate extension, falling back to `./extension.node`', ex); + // In smoke tests, we do not want to load the out/extension.node. + realEntryPoint = eval('require')('./extension.node'); + return realEntryPoint.activate(context); + } +} + +export function deactivate(): Thenable { + return realEntryPoint.deactivate(); +} diff --git a/src/kernels/raw/session/zeromq.node.ts b/src/kernels/raw/session/zeromq.node.ts index 2ef3f266b58..8845b46fda5 100644 --- a/src/kernels/raw/session/zeromq.node.ts +++ b/src/kernels/raw/session/zeromq.node.ts @@ -13,13 +13,15 @@ const zeromqModuleName = `${'zeromq'}`; export function getZeroMQ(): typeof import('zeromq') { try { const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require; - const zmq = requireFunc(zeromqModuleName); + const zmq: typeof import('zeromq') = requireFunc(zeromqModuleName); + // We do not want to block the process from exiting if there are any pending messages. + zmq.context.blocky = false; sendZMQTelemetry(false).catch(noop); return zmq; } catch (e) { try { const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require; - const zmq = requireFunc(path.join(EXTENSION_ROOT_DIR, 'out', 'node_modules', 'zeromqold')); + const zmq = requireFunc(path.join(EXTENSION_ROOT_DIR, 'dist', 'node_modules', 'zeromqold')); traceInfo('ZMQ loaded via fallback mechanism.'); sendZMQTelemetry(false, true, e.message || e.toString()).catch(noop); return zmq; @@ -38,8 +40,8 @@ export function getZeroMQ(): typeof import('zeromq') { */ async function getLocalZmqBinaries() { try { - const zmqFolder = path.join(EXTENSION_ROOT_DIR, 'out', 'node_modules', 'zeromq', 'prebuilds'); - if (!(await fs.pathExists(path.join(EXTENSION_ROOT_DIR, 'out', 'node_modules')))) { + const zmqFolder = path.join(EXTENSION_ROOT_DIR, 'dist', 'node_modules', 'zeromq', 'prebuilds'); + if (!(await fs.pathExists(path.join(EXTENSION_ROOT_DIR, 'dist', 'node_modules')))) { // We're in dev mode. return; } diff --git a/src/notebooks/controllers/vscodeNotebookController.ts b/src/notebooks/controllers/vscodeNotebookController.ts index 41244aa0873..1642fccc9fa 100644 --- a/src/notebooks/controllers/vscodeNotebookController.ts +++ b/src/notebooks/controllers/vscodeNotebookController.ts @@ -522,7 +522,7 @@ export class VSCodeNotebookController implements Disposable, IVSCodeNotebookCont new NotebookRendererScript( Uri.joinPath( this.context.extensionUri, - 'out', + 'dist', 'webviews', 'webview-side', 'widgetTester', diff --git a/src/platform/constants.ts b/src/platform/constants.ts index f95a6e751df..d629ccbb9f5 100644 --- a/src/platform/constants.ts +++ b/src/platform/constants.ts @@ -5,17 +5,9 @@ export const HiddenFileFormatString = '_HiddenFile_{0}.py'; export const MillisecondsInADay = 24 * 60 * 60 * 1_000; -declare var IS_PRE_RELEASE_VERSION_OF_JUPYTER_EXTENSION: 'true' | 'false'; export function isPreReleaseVersion() { try { - if (IS_PRE_RELEASE_VERSION_OF_JUPYTER_EXTENSION === 'true') { - return 'true'; - } else if (IS_PRE_RELEASE_VERSION_OF_JUPYTER_EXTENSION === 'false') { - return 'false'; - } else { - // No idea, possible webpack is not replacing the value, meaning we're in dev mode. - return 'true'; - } + return require('vscode-jupyter-relese-version').isPreRelesVersionOfJupyterExtension === true ? 'true' : 'false'; } catch { // Dev version is treated as pre-release. return 'true'; diff --git a/src/standalone/intellisense/languageServer.node.ts b/src/standalone/intellisense/languageServer.node.ts index 765e8dfc84a..147469864f3 100644 --- a/src/standalone/intellisense/languageServer.node.ts +++ b/src/standalone/intellisense/languageServer.node.ts @@ -18,7 +18,7 @@ import type { } from 'vscode-languageclient/node'; import * as path from '../../platform/vscode-path/path'; import * as fs from 'fs-extra'; -import { createNotebookMiddleware, NotebookMiddleware } from '@vscode/jupyter-lsp-middleware'; +import type { NotebookMiddleware } from '@vscode/jupyter-lsp-middleware'; import uuid from 'uuid/v4'; import { NOTEBOOK_SELECTOR, PYTHON_LANGUAGE } from '../../platform/common/constants'; import { traceInfo, traceInfoIfCI } from '../../platform/logging'; @@ -167,6 +167,7 @@ export class LanguageServer implements Disposable { let languageClient: LanguageClient | undefined; const outputChannel = window.createOutputChannel(`${interpreter.displayName || 'notebook'}-languageserver`); const interpreterId = getComparisonKey(interpreter.uri); + const { createNotebookMiddleware } = await import('@vscode/jupyter-lsp-middleware'); const middleware = createNotebookMiddleware( () => languageClient, () => noop, // Don't trace output. Slows things down too much diff --git a/src/test/standardTest.node.ts b/src/test/standardTest.node.ts index 3e5a926c12e..6188b0871df 100644 --- a/src/test/standardTest.node.ts +++ b/src/test/standardTest.node.ts @@ -59,6 +59,14 @@ async function createTempDir() { }); } +// function updatePackageJson() { +// const file = path.join(EXTENSION_ROOT_DIR_FOR_TESTS, 'package.json'); +// const packageJson = JSON.parse(fs.readFileSync(file).toString()) as { main: string; browser: string }; +// packageJson.main = './out/extension.node.js'; +// packageJson.browser = './out/extension.web.bundle.js'; +// fs.writeFileSync(file, JSON.stringify(packageJson, undefined, 4)); +// } + /** * Smoke tests & tests running in VSCode require Python extension to be installed. */ @@ -159,6 +167,7 @@ async function getExtensionsDir(): Promise { async function start() { console.log('*'.repeat(100)); console.log('Start Standard tests'); + // updatePackageJson(); const platform = computePlatform(); const vscodeExecutablePath = await downloadAndUnzipVSCode(channel, platform); const baseLaunchArgs = requiresPythonExtensionToBeInstalled() ? [] : ['--disable-extensions']; diff --git a/src/webviews/extension-side/dataviewer/dataViewer.ts b/src/webviews/extension-side/dataviewer/dataViewer.ts index db9909422bf..25b0c95d8ab 100644 --- a/src/webviews/extension-side/dataviewer/dataViewer.ts +++ b/src/webviews/extension-side/dataviewer/dataViewer.ts @@ -81,7 +81,7 @@ export class DataViewer extends WebviewPanelHost implements @inject(IDataScienceErrorHandler) readonly errorHandler: IDataScienceErrorHandler, @inject(IExtensionContext) readonly context: IExtensionContext ) { - const dataExplorerDir = joinPath(context.extensionUri, 'out', 'webviews', 'webview-side', 'viewers'); + const dataExplorerDir = joinPath(context.extensionUri, 'dist', 'webviews', 'webview-side', 'viewers'); super( configuration, provider, diff --git a/src/webviews/extension-side/plotting/plotViewer.ts b/src/webviews/extension-side/plotting/plotViewer.ts index ee4d90fc88a..b5db7601e28 100644 --- a/src/webviews/extension-side/plotting/plotViewer.ts +++ b/src/webviews/extension-side/plotting/plotViewer.ts @@ -36,7 +36,7 @@ export class PlotViewer extends WebviewPanelHost implements @inject(IExtensionContext) readonly context: IExtensionContext ) { const startupTimer = new StopWatch(); - const plotDir = joinPath(context.extensionUri, 'out', 'webviews', 'webview-side', 'viewers'); + const plotDir = joinPath(context.extensionUri, 'dist', 'webviews', 'webview-side', 'viewers'); super( configuration, provider, diff --git a/src/webviews/extension-side/variablesView/variableView.ts b/src/webviews/extension-side/variablesView/variableView.ts index 04321c274f0..f6b7a328da8 100644 --- a/src/webviews/extension-side/variablesView/variableView.ts +++ b/src/webviews/extension-side/variablesView/variableView.ts @@ -59,7 +59,7 @@ export class VariableView extends WebviewViewHost imp private readonly extensions: IExtensions, private readonly experiments: IExperimentService ) { - const variableViewDir = joinPath(context.extensionUri, 'out', 'webviews', 'webview-side', 'viewers'); + const variableViewDir = joinPath(context.extensionUri, 'dist', 'webviews', 'webview-side', 'viewers'); super( configuration, workspaceService, diff --git a/src/webviews/webview-side/data-explorer/index.tsx b/src/webviews/webview-side/data-explorer/index.tsx index 11d31dbf500..d62397d57da 100644 --- a/src/webviews/webview-side/data-explorer/index.tsx +++ b/src/webviews/webview-side/data-explorer/index.tsx @@ -1,13 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -console.error('Define jQuery on top'); const jquery = require('slickgrid/lib/jquery-1.11.2.min'); // eslint-disable-next-line @typescript-eslint/no-explicit-any (globalThis as any).jQuery = jquery; // eslint-disable-next-line @typescript-eslint/no-explicit-any (globalThis as any).$ = jquery; -console.error('Define jQuery on top', jQuery); // This must be on top, do not change. Required by webpack. import '../common/main';