Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .licenserc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@ header:
- '**/build'
- '**/.*'
- 'LICENSE'
- tsconfig.tsbuildinfo
- node_modules/**

comment: on-failure
3 changes: 3 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ repos:
hooks:
- id: codespell
args: ["-L", "sur,nd"]
exclude: jupyter_builder/yarn.js
exclude: builder/tsconfig.tsbuildinfo
exclude: end-of-file-fixer

- repo: https://github.com/pre-commit/pygrep-hooks
rev: "v1.10.0"
Expand Down
6 changes: 6 additions & 0 deletions builder/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# @jupyterlab/builder

A JupyterLab package which provides functions that are used to compile
and build JupyterLab extensions.

This package is only intended for use within Node.js environments.
119 changes: 119 additions & 0 deletions builder/metadata_schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
{
"title": "JupyterLab Package Metadata",
"version": "0.1.0",
"description": "JupyterLab package.json settings.",
"definitions": {
"extension": {
"type": ["boolean", "string"]
},
"relativePath": {
"type": ["string", "null"]
},
"sharedObject": {
"description": "Modules that should be shared in the share scope. Property names are used to match requested modules in this compilation. Relative requests are resolved, module requests are matched unresolved, absolute paths will match resolved requests.",
"type": "object",
"additionalProperties": {
"description": "Module sharing information.",
"anyOf": [
{
"description": "Module is not shared. A local copy will be bundled and used.",
"enum": [false]
},
{
"$ref": "#/definitions/sharedConfig"
}
]
}
},
"sharedConfig": {
"description": "Configuration data for package sharing.",
"type": "object",
"additionalProperties": false,
"properties": {
"bundled": {
"description": "The module will be bundled and provided to the system as a shared module (the system will use the latest bundled version from any extension). This bundled local copy will be used if the system version does not match the requiredVersion. Defaults to true",
"type": "boolean",
"default": "true"
},
"requiredVersion": {
"description": "Version requirement from module in share scope. Defaults to the version required in the package.json, or false if the version cannot be determined. You can specify it here in case the version cannot be determined automatically.",
"anyOf": [
{
"description": "No version requirement check.",
"enum": [false]
},
{
"description": "The semver range required for the shared module.",
"type": "string"
}
]
},
"singleton": {
"description": "Allow only a single version of the shared module in share scope. Will only use the system provided version, and not fall back to a local bundled copy. Default is false.",
"type": "boolean",
"default": "false"
},
"strictVersion": {
"description": "Throw an error (instead of a warning) if the shared module version does not satisfy the requiredVersion. Defaults to false when forced to use the system-provided version of the module (i.e., singleton is true or bundled is false), in which case we will just print a console warning if the system-provided version does not satisfy the requiredVersion. This has no effect if requiredVersion is false.",
"type": "boolean"
}
}
}
},
"properties": {
"extension": {
"title": "Extension",
"description": "Presence of or relative path to a standard JupyterLab extension",
"$ref": "#/definitions/extension",
"default": false
},
"mimeExtension": {
"title": "Mime extension",
"description": "Presence of or relative path to a JupyterLab MIME renderer extension",
"$ref": "#/definitions/extension",
"default": false
},
"themePath": {
"title": "Theme path",
"description": "The relative path to theme files",
"$ref": "#/definitions/relativePath",
"default": null
},
"schemaDir": {
"title": "Schema directory",
"description": "The relative path to schema files",
"$ref": "#/definitions/relativePath",
"default": null
},
"outputDir": {
"title": "Output directory",
"description": "The relative path to the static assets",
"$ref": "#/definitions/relativePath",
"default": "static"
},
"webpackConfig": {
"title": "Custom Webpack config",
"description": "The relative path to a custom webpack config",
"$ref": "#/definitions/relativePath",
"default": null
},
"sharedPackages": {
"description": "Modules that should be shared in the share scope. When provided, property names are used to match requested modules in this compilation.",
"ref": "#/definitions/sharedObject"
},
"discovery": {
"title": "Discovery metadata",
"description": "Discovery metadata used to for companion packages",
"type": "object"
},
"disabledExtensions": {
"title": "List of disabled extension modules and/or regex patterns for extension ids",
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false,
"type": "object"
}
12 changes: 12 additions & 0 deletions builder/node_modules/.bin/build-labextension

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions builder/node_modules/.bin/build-labextension.cmd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

79 changes: 79 additions & 0 deletions builder/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
{
"name": "@jupyterlab/builder",
"version": "4.1.2",
"description": "JupyterLab - Extension Builder",
"homepage": "https://github.com/jupyterlab/jupyterlab",
"bugs": {
"url": "https://github.com/jupyterlab/jupyterlab/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/jupyterlab/jupyterlab.git"
},
"license": "BSD-3-Clause",
"author": "Project Jupyter",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"bin": {
"build-labextension": "./lib/build-labextension.js"
},
"directories": {
"lib": "lib/"
},
"files": [
"metadata_schema.json",
"lib/*.d.ts",
"lib/*.js.map",
"lib/*.js",
"src/**/*.{ts,tsx}"
],
"scripts": {
"build": "tsc",
"clean": "rimraf lib && rimraf tsconfig.tsbuildinfo",
"watch": "tsc -w --listEmittedFiles"
},
"dependencies": {
"@lumino/algorithm": "^2.0.1",
"@lumino/application": "^2.3.0",
"@lumino/commands": "^2.2.0",
"@lumino/coreutils": "^2.1.2",
"@lumino/disposable": "^2.1.2",
"@lumino/domutils": "^2.0.1",
"@lumino/dragdrop": "^2.1.4",
"@lumino/messaging": "^2.0.1",
"@lumino/properties": "^2.0.1",
"@lumino/signaling": "^2.1.2",
"@lumino/virtualdom": "^2.0.1",
"@lumino/widgets": "^2.3.1",
"ajv": "^8.12.0",
"commander": "^9.4.1",
"css-loader": "^6.7.1",
"duplicate-package-checker-webpack-plugin": "^3.0.0",
"fs-extra": "^10.1.0",
"glob": "~7.1.6",
"license-webpack-plugin": "^2.3.14",
"mini-css-extract-plugin": "^2.7.0",
"mini-svg-data-uri": "^1.4.4",
"path-browserify": "^1.0.0",
"process": "^0.11.10",
"source-map-loader": "~1.0.2",
"style-loader": "~3.3.1",
"supports-color": "^7.2.0",
"terser-webpack-plugin": "^5.3.7",
"webpack": "^5.76.1",
"webpack-cli": "^5.0.1",
"webpack-merge": "^5.8.0",
"worker-loader": "^3.0.2"
},
"devDependencies": {
"@types/fs-extra": "^9.0.1",
"@types/glob": "^7.1.1",
"@types/node": "^18.11.18",
"@types/supports-color": "^5.3.0",
"rimraf": "~5.0.5",
"typescript": "~5.1.6"
},
"publishConfig": {
"access": "public"
}
}
149 changes: 149 additions & 0 deletions builder/src/build-labextension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#!/usr/bin/env node
/* -----------------------------------------------------------------------------
| Copyright (c) Jupyter Development Team.
| Distributed under the terms of the Modified BSD License.
|----------------------------------------------------------------------------*/

// Build an extension

// Inputs:
// Path to extension (required)
// Dev vs prod (dev is default)
// Output path (defaults to <extension>/build)

// Outputs
// Webpack build assets

///////////////////////////////////////////////////////
// Portions of the below code handling watch mode and displaying output were
// adapted from the https://github.com/webpack/webpack-cli project, which has
// an MIT license (https://github.com/webpack/webpack-cli/blob/4dc6dfbf29da16e61745770f7b48638963fb05c5/LICENSE):
//
// Copyright JS Foundation and other contributors
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// 'Software'), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
///////////////////////////////////////////////////////

import * as path from 'path';
import { program as commander } from 'commander';
import webpack from 'webpack';
import generateConfig from './extensionConfig';
import { stdout as colors } from 'supports-color';

commander
.description('Build an extension')
.option('--development', 'build in development mode (implies --source-map)')
.option('--source-map', 'generate source maps')
.requiredOption('--core-path <path>', 'the core package directory')
.option(
'--static-url <url>',
'url for build assets, if hosted outside the built extension'
)
.option('--watch')
.action(async (options, command) => {
const mode = options.development ? 'development' : 'production';
const corePath = path.resolve(options.corePath || process.cwd());
const packagePath = path.resolve(command.args[0]);
const devtool = options.sourceMap ? 'source-map' : undefined;

const config = generateConfig({
packagePath,
mode,
corePath,
staticUrl: options.staticUrl,
devtool,
watchMode: options.watch
});
const compiler = webpack(config);

let lastHash: string | null = null;

function compilerCallback(err: any, stats: any) {
if (!options.watch || err) {
// Do not keep cache anymore
compiler.purgeInputFileSystem();
}

if (err) {
console.error(err.stack || err);
if (err.details) {
console.error(err.details);
}
throw new Error(err.details);
}

const info = stats.toJson();

if (stats.hasErrors()) {
console.error(info.errors);
if (!options.watch) {
process.exit(2);
}
}

if (stats.hash !== lastHash) {
lastHash = stats.hash;
const statsString = stats.toString({ colors });
const delimiter = '';
if (statsString) {
process.stdout.write(`${statsString}\n${delimiter}`);
}
}
}

if (options.watch) {
compiler.hooks.watchRun.tap('WebpackInfo', () => {
console.error('\nWatch Compilation starting…\n');
});
compiler.hooks.done.tap('WebpackInfo', () => {
console.error('\nWatch Compilation finished\n');
});
} else {
compiler.hooks.run.tap('WebpackInfo', () => {
console.error('\nCompilation starting…\n');
});
compiler.hooks.done.tap('WebpackInfo', () => {
console.error('\nCompilation finished\n');
});
}

if (options.watch) {
compiler.watch(config[0].watchOptions || {}, compilerCallback);
console.error('\nwebpack is watching the files…\n');
} else {
compiler.run((err: any, stats: any) => {
if (compiler.close) {
compiler.close((err2: any) => {
compilerCallback(err || err2, stats);
});
} else {
compilerCallback(err, stats);
}
});
}
});

commander.parse(process.argv);

// If no arguments supplied
if (!process.argv.slice(2).length) {
commander.outputHelp();
process.exit(1);
}
Loading