From 394c3f38d1f4c7dd66791de5726c6faa69098441 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 30 Jan 2023 11:04:44 +1100 Subject: [PATCH 1/2] Remove moment from bundle --- build/ci/postInstall.js | 33 +++++++++++++++++++ build/webpack/moment.js | 27 +++++++++++++++ .../webpack/webpack.extension.node.config.js | 4 --- build/webpack/webpack.extension.web.config.js | 7 ++-- 4 files changed, 62 insertions(+), 9 deletions(-) create mode 100644 build/webpack/moment.js diff --git a/build/ci/postInstall.js b/build/ci/postInstall.js index dd17580bab1..981bd60917c 100644 --- a/build/ci/postInstall.js +++ b/build/ci/postInstall.js @@ -161,9 +161,42 @@ function fixUIFabricForTS49() { }); } +/** + * Ensures that moment is not used by any other npm package other than @jupyterlab/coreutils. + * See comments here build/webpack/moment.js + */ +function verifyMomentIsOnlyUsedByJupyterLabCoreUtils() { + const packageLock = require(path.join(__dirname, '..', '..', 'package-lock.json')); + const packagesAllowedToUseMoment = ['node_modules/@jupyterlab/coreutils', '@jupyterlab/coreutils']; + const otherPackagesUsingMoment = []; + ['packages', 'dependencies'].forEach((key) => { + if (!(key in packageLock)) { + throw new Error(`Invalid package-lock.json, as it does not contain the key '${key}'`); + } + const packages = packageLock[key]; + Object.keys(packages).forEach((packageName) => { + if (packagesAllowedToUseMoment.includes(packageName)) { + return; + } + ['dependencies', 'requires'].forEach((dependencyKey) => { + if (dependencyKey in packages[packageName]) { + const dependenciesOfPackage = packages[packageName][dependencyKey]; + if ('moment' in dependenciesOfPackage) { + otherPackagesUsingMoment.push(`${key}.${dependencyKey}.${packageName}`); + } + } + }); + }); + }); + if (otherPackagesUsingMoment.length > 0) { + throw new Error(`Moment is being used by another package (${otherPackagesUsingMoment.join(', ')}).`); + } +} + fixUIFabricForTS49(); fixJupyterLabRenderers(); makeVariableExplorerAlwaysSorted(); createJupyterKernelWithoutSerialization(); updateJSDomTypeDefinition(); fixStripComments(); +verifyMomentIsOnlyUsedByJupyterLabCoreUtils(); diff --git a/build/webpack/moment.js b/build/webpack/moment.js new file mode 100644 index 00000000000..8080e13640f --- /dev/null +++ b/build/webpack/moment.js @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// This file has been handcrafted to override the use of moment.js. +// Moment is only used in @jupyterlab/coreutils/lib/time.js +// & that code is not used anywhere in any code path. +// Hence we create a dummy moment object that does nothing. +// & we have checks on CI that ensures no other npm package or other code does not in-directly or directly use moment. + +export default function (dateTime) { + return { + formatNow: () => { + try { + return dateTime.toLocaleString(); + } catch { + return `${dateTime}`; + } + }, + format: () => { + try { + return dateTime.toLocaleTimeString(); + } catch { + return `${dateTime}`; + } + } + }; +} diff --git a/build/webpack/webpack.extension.node.config.js b/build/webpack/webpack.extension.node.config.js index 1fc01c20fc2..3181d27d728 100644 --- a/build/webpack/webpack.extension.node.config.js +++ b/build/webpack/webpack.extension.node.config.js @@ -130,10 +130,6 @@ const config = { new copyWebpackPlugin({ patterns: [{ from: './node_modules/zeromq/**/*.json' }] }), new copyWebpackPlugin({ patterns: [{ from: './node_modules/node-gyp-build/**/*' }] }), new copyWebpackPlugin({ patterns: [{ from: './node_modules/@vscode/jupyter-ipywidgets7/dist/*.js' }] }), - new webpack.IgnorePlugin({ - resourceRegExp: /^\.\/locale$/, - contextRegExp: /moment$/ - }), new webpack.DefinePlugin({ IS_PRE_RELEASE_VERSION_OF_JUPYTER_EXTENSION: JSON.stringify( typeof process.env.IS_PRE_RELEASE_VERSION_OF_JUPYTER_EXTENSION === 'string' diff --git a/build/webpack/webpack.extension.web.config.js b/build/webpack/webpack.extension.web.config.js index a514eb7b6fa..9da51766451 100644 --- a/build/webpack/webpack.extension.web.config.js +++ b/build/webpack/webpack.extension.web.config.js @@ -114,10 +114,6 @@ const config = { ) }), new CleanTerminalPlugin(), - new webpack.IgnorePlugin({ - resourceRegExp: /^\.\/locale$/, - contextRegExp: /moment$/ - }), new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }) @@ -130,7 +126,8 @@ const config = { ], alias: { // provides alternate implementation for node module and source files - fs: './fs-empty.js' + fs: './fs-empty.js', + moment: path.join(__dirname, 'moment.js') }, fallback: { os: require.resolve('os-browserify') From 165e9824a51f8eaf587c2e4d6dcb7b6f4165daec Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 30 Jan 2023 11:04:55 +1100 Subject: [PATCH 2/2] oops --- build/webpack/webpack.extension.node.config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/webpack/webpack.extension.node.config.js b/build/webpack/webpack.extension.node.config.js index 3181d27d728..2506ddacfe2 100644 --- a/build/webpack/webpack.extension.node.config.js +++ b/build/webpack/webpack.extension.node.config.js @@ -143,7 +143,8 @@ const config = { // Pointing pdfkit to a dummy js file so webpack doesn't fall over. // Since pdfkit has been externalized (it gets updated with the valid code by copying the pdfkit files // into the right destination). - pdfkit: path.resolve(__dirname, 'pdfkit.js') + pdfkit: path.resolve(__dirname, 'pdfkit.js'), + moment: path.join(__dirname, 'moment.js') }, extensions: ['.ts', '.js'], plugins: [new tsconfig_paths_webpack_plugin.TsconfigPathsPlugin({ configFile: configFileName })],