diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e651ab2..f223fe1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -39,5 +39,6 @@ jobs: run: | source "$CONDA/etc/profile.d/conda.sh" conda activate jupyterlab-module-federation + pip install jupyterlab==2.1.5 python run.py diff --git a/.gitignore b/.gitignore index fc7225e..2302e97 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,3 @@ stats.json node_modules core_package/build md_package/build -log.json diff --git a/build-ext.js b/build-ext.js index aa42959..fa26df1 100644 --- a/build-ext.js +++ b/build-ext.js @@ -36,7 +36,9 @@ commander const webpack = require.resolve('webpack-cli'); let cmdText = `${webpack} --config webpack.config.ext.js`; - run(cmdText, { env: { ...process.env, OUTPUT_PATH: output, PACKAGE_PATH: packagePath, NODE_ENV: node_env } }); + const env = { OUTPUT_PATH: output, PACKAGE_PATH: packagePath, NODE_ENV: node_env }; + console.log(env); + run(cmdText, { env: { ...process.env, ...env } }); } ); diff --git a/core_package/package.json b/core_package/package.json index 445d6c6..2d8ab94 100644 --- a/core_package/package.json +++ b/core_package/package.json @@ -54,7 +54,6 @@ "@jupyterlab/logconsole-extension": "~3.0.0-alpha.4", "@jupyterlab/mainmenu": "~3.0.0-alpha.4", "@jupyterlab/mainmenu-extension": "~3.0.0-alpha.4", - "@jupyterlab/markdownviewer": "~3.0.0-alpha.4", "@jupyterlab/mathjax2": "~3.0.0-alpha.4", "@jupyterlab/mathjax2-extension": "~3.0.0-alpha.4", "@jupyterlab/metapackage": "~3.0.0-alpha.4", @@ -145,7 +144,6 @@ "@jupyterlab/launcher", "@jupyterlab/logconsole", "@jupyterlab/mainmenu", - "@jupyterlab/markdownviewer", "@jupyterlab/notebook", "@jupyterlab/rendermime", "@jupyterlab/rendermime-interfaces", diff --git a/main.py b/main.py index fdaa254..2e8dc15 100644 --- a/main.py +++ b/main.py @@ -3,6 +3,7 @@ from jupyterlab_server import LabServerApp, LabConfig from jupyterlab_server.server import FileFindHandler, APIHandler + from notebook.utils import url_path_join as ujoin, url_escape import json import os @@ -10,7 +11,7 @@ from tornado.web import StaticFileHandler -HERE = os.path.dirname(__file__) +HERE = os.path.abspath(os.path.dirname(__file__)) # Turn off the Jupyter configuration system so configuration files on disk do # not affect this app. This helps this app to truly be standalone. @@ -24,10 +25,24 @@ class SettingHandler(APIHandler): def get(self, schema_name=""): path = os.path.join(HERE, 'node_modules/@jupyterlab/markdownviewer-extension/schema/plugin.json') + with open(path) as fid: - data = fid.read() - return self.finish(data) + schema = fid.read() + + # copy-pasta of typical response for now. + result = {"id":"@jupyterlab/markdownviewer-extension:plugin","raw":"{}","schema":{"jupyter.lab.setting-icon":"ui-components:markdown","jupyter.lab.setting-icon-label":"Markdown Viewer","title":"Markdown Viewer","description":"Markdown viewer settings.","definitions":{"fontFamily":{"type":["string","null"]},"fontSize":{"type":["integer","null"],"minimum":1,"maximum":100},"lineHeight":{"type":["number","null"]},"lineWidth":{"type":["number","null"]},"hideFrontMatter":{"type":"boolean"},"renderTimeout":{"type":"number"}},"properties":{"fontFamily":{"title":"Font Family","description":"The font family used to render markdown.\nIf `null`, value from current theme is used.","$ref":"#/definitions/fontFamily","default":None},"fontSize":{"title":"Font Size","description":"The size in pixel of the font used to render markdown.\nIf `null`, value from current theme is used.","$ref":"#/definitions/fontSize","default":None},"lineHeight":{"title":"Line Height","description":"The line height used to render markdown.\nIf `null`, value from current theme is used.","$ref":"#/definitions/lineHeight","default":None},"lineWidth":{"title":"Line Width","description":"The text line width expressed in CSS ch units.\nIf `null`, lines fit the viewport width.","$ref":"#/definitions/lineWidth","default":None},"hideFrontMatter":{"title":"Hide Front Matter","description":"Whether to hide YAML front matter.\nThe YAML front matter must be placed at the top of the document,\nstarted by a line of three dashes (---) and ended by a line of\nthree dashes (---) or three points (...).","$ref":"#/definitions/hideFrontMatter","default":True},"renderTimeout":{"title":"Render Timeout","description":"The render timeout in milliseconds.","$ref":"#/definitions/renderTimeout","default":1000}},"additionalProperties":False,"type":"object"},"settings":{},"version":"2.2.0"} + + return self.finish(result) + +class ExtensionHandler(FileFindHandler): + + def get(self, path): + extname, _, rest = path.partition('/') + dirname = 'md_package' if extname == 'example-federated-md' else 'middle_package' + path = os.path.join(dirname, 'build', rest) + return super().get(path) + class ExampleApp(LabServerApp): base_url = '/foo' @@ -53,11 +68,9 @@ def init_webapp(self): # Handle md ext assets web_app = self.web_app base_url = web_app.settings['base_url'] - static_path = ujoin(base_url, 'example', 'labextensions', '@jupyterlab', 'example-federated-md', '(.*)') - static_dir = os.path.join(HERE, 'md_package', 'build') - web_app.add_handlers('.*$', [(static_path, FileFindHandler, { - 'path': static_dir, - })]) + + static_path = ujoin(base_url, 'example', 'labextensions', '@jupyterlab', '(.*)') + web_app.add_handlers('.*$', [(static_path, ExtensionHandler, { 'path': HERE })]) ## Handle the specific setting static_path = ujoin(base_url, 'example', 'api', 'settings', '@jupyterlab', '(.*)') diff --git a/md_package/index.js b/md_package/index.js index e1be0d5..fa2c550 100644 --- a/md_package/index.js +++ b/md_package/index.js @@ -31,8 +31,9 @@ const plugin = { /** * Activate the markdown viewer plugin. */ -function activate(app, restorer, rendermime, settingRegistry) { +function activate(app, restorer, rendermime, settingRegistry, middleToken) { const { commands, docRegistry } = app; + // Add the markdown renderer factory. rendermime.addFactory(markdownRendererFactory); const namespace = 'markdownviewer-widget'; diff --git a/md_package/package.json b/md_package/package.json index e223ce8..58e351d 100644 --- a/md_package/package.json +++ b/md_package/package.json @@ -10,6 +10,7 @@ "@jupyterlab/application": "^3.0.0-alpha.4", "@jupyterlab/apputils": "^3.0.0-alpha.4", "@jupyterlab/coreutils": "^5.0.0-alpha.4", + "@jupyterlab/example-federated-middle": "~2.1.0", "@jupyterlab/markdownviewer": "^3.0.0-alpha.4", "@jupyterlab/rendermime": "^3.0.0-alpha.4", "@jupyterlab/settingregistry": "^3.0.0-alpha.4" @@ -24,6 +25,7 @@ }, "jupyterlab": { "extension": true, - "schemaDir": "schema" + "schemaDir": "schema", + "singletonPackages": ["@jupyterlab/example-federated-middle"] } } diff --git a/middle_package/extension.js b/middle_package/extension.js new file mode 100644 index 0000000..432de3e --- /dev/null +++ b/middle_package/extension.js @@ -0,0 +1,12 @@ +const IMiddleToken = require('./index').IMiddleToken; + +module.exports = [{ + id: '@jupyterlab/example-federated-middle', + autoStart: true, + provides: IMiddleToken, + activate: function (app) { + console.log('JupyterLab extension middle is activated!'); + console.log(app.commands); + return 'hello'; + } +}]; diff --git a/middle_package/index.js b/middle_package/index.js new file mode 100644 index 0000000..07312d0 --- /dev/null +++ b/middle_package/index.js @@ -0,0 +1,11 @@ +// Need a token here + +// Copyright (c) Jupyter Development Team. +// Distributed under the terms of the Modified BSD License. +import { Token } from '@lumino/coreutils'; +/* tslint:disable */ +/** + * The path tracker token. + */ +export const IMiddleToken = new Token('@jupyterlab/example-federated-middle2'); +//# sourceMappingURL=tokens.js.map diff --git a/middle_package/package.json b/middle_package/package.json new file mode 100644 index 0000000..0557a0c --- /dev/null +++ b/middle_package/package.json @@ -0,0 +1,24 @@ +{ + "name": "@jupyterlab/example-federated-middle", + "version": "2.1.0", + "private": true, + "scripts": { + "prepublishOnly": "npm run build", + "watch": "npm run clean && webpack --watch" + }, + "dependencies": { + "@jupyterlab/coreutils": "^5.0.0-alpha.4" + }, + "devDependencies": { + "rimraf": "~3.0.0", + "typedoc": "^0.17.7", + "typescript": "~3.9.2" + }, + "publishConfig": { + "access": "public" + }, + "jupyterlab": { + "extension": "./extension.js", + "schemaDir": "schema" + } +} diff --git a/package.json b/package.json index 82fb0bf..f5c7e0c 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,14 @@ "version": "2.1.0", "private": true, "scripts": { - "build": "npm run build:core && npm run build:ext", + "build": "npm run build:core && npm run build:middle && npm run build:md", "build:core": "cd core_package && npm run build", - "build:ext": "node build-ext.js ./md_package", + "build:middle": "node build-ext.js ./middle_package", + "build:md": "node build-ext.js ./md_package", "clean:core": "cd core_package && npm run clean", - "clean:ext": "rimraf md_package/build", - "clean": "npm run clean:ext && npm run clean:core" + "clean:md": "rimraf md_package/build", + "clean:middle": "rimraf middle_package/build", + "clean": "npm run clean:md && npm run clean:middle && npm run clean:core" }, "devDependencies": { "rimraf": "^3.0.2", diff --git a/templates/index.html b/templates/index.html index a1202b9..f8b4494 100644 --- a/templates/index.html +++ b/templates/index.html @@ -16,7 +16,8 @@ diff --git a/webpack.config.ext.js b/webpack.config.ext.js index 879abf2..d60e963 100644 --- a/webpack.config.ext.js +++ b/webpack.config.ext.js @@ -67,15 +67,17 @@ const extras = Build.ensureAssets({ output: outputPath }); -let entryPoint = data.jupyterlab.extension ?? data.jupyterlab.mimeExtension; - - if (entryPoint === true) { - // Use require to get the entry point - entryPoint = require.resolve(packagePath); - } else { - // Use the path to get the entry point - entryPoint = path.join(packagePath, entryPoint); - } +// Handle the extension entry point and the lib entry point, if different +let extPath = data.jupyterlab.extension ?? data.jupyterlab.mimeExtension; +const index = require.resolve(packagePath); +const exposes = { + './index': index, + './extension': index +} + +if (extPath !== true) { + exposes['./extension'] = path.join(packagePath, extPath); +} const coreData = require('./core_package/package.json'); @@ -97,7 +99,7 @@ Object.keys(data.dependencies).forEach((element) => { // Remove non-shared. data.jupyterlab.nonSharedPackages?.forEach((element) => { delete shared[element]; - }); +}); // Start with core singletons. coreData.jupyterlab.singletonPackages.forEach((element) => { @@ -113,7 +115,7 @@ data.jupyterlab.singletonPackages?.forEach((element) => { if (!shared[element]) { shared[element] = {}; } - shared[element].singleton = true; + shared[element].import = false; }); // Remove non-singletons. @@ -126,6 +128,12 @@ data.jupyterlab.nonSingletonPackages?.forEach((element) => { // Ensure a clean output directory. fs.rmdirSync(outputPath, { recursive: true }); +fs.mkdirSync(outputPath); + +// Make a bootstrap entrypoint +const entryPoint = path.join(outputPath, 'bootstrap.js'); +const bootstrap = 'import("' + exposes['./extension'] + '");' +fs.writeFileSync(entryPoint, bootstrap); module.exports = [ { @@ -146,9 +154,7 @@ module.exports = [ name: ['_JUPYTERLAB', data.name] }, filename: 'remoteEntry.js', - exposes: { - './index': entryPoint - }, + exposes, shared, }), new webpack.DefinePlugin({ @@ -159,6 +165,5 @@ module.exports = [ } ].concat(extras); -// TODO: remove debug log -// console.log(module.exports); -fs.writeFileSync('log.json', JSON.stringify(module.exports, null, ' ')); +const logPath = path.join(outputPath, 'build_log.json'); +fs.writeFileSync(logPath, JSON.stringify(module.exports, null, ' '));