UNPKG

10.9 kBJavaScriptView Raw
1"use strict";
2// Copyright (c) Jupyter Development Team.
3// Distributed under the terms of the Modified BSD License.
4var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5 if (k2 === undefined) k2 = k;
6 var desc = Object.getOwnPropertyDescriptor(m, k);
7 if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8 desc = { enumerable: true, get: function() { return m[k]; } };
9 }
10 Object.defineProperty(o, k2, desc);
11}) : (function(o, m, k, k2) {
12 if (k2 === undefined) k2 = k;
13 o[k2] = m[k];
14}));
15var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
16 Object.defineProperty(o, "default", { enumerable: true, value: v });
17}) : function(o, v) {
18 o["default"] = v;
19});
20var __importStar = (this && this.__importStar) || function (mod) {
21 if (mod && mod.__esModule) return mod;
22 var result = {};
23 if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
24 __setModuleDefault(result, mod);
25 return result;
26};
27var __importDefault = (this && this.__importDefault) || function (mod) {
28 return (mod && mod.__esModule) ? mod : { "default": mod };
29};
30Object.defineProperty(exports, "__esModule", { value: true });
31const path = __importStar(require("path"));
32const webpack = __importStar(require("webpack"));
33const build_1 = require("./build");
34const webpack_plugins_1 = require("./webpack-plugins");
35const webpack_merge_1 = require("webpack-merge");
36const fs = __importStar(require("fs-extra"));
37const glob = __importStar(require("glob"));
38const ajv_1 = __importDefault(require("ajv"));
39const baseConfig = require('./webpack.config.base');
40const { ModuleFederationPlugin } = webpack.container;
41function generateConfig({ packagePath = '', corePath = '', staticUrl = '', mode = 'production', devtool = mode === 'development' ? 'source-map' : undefined, watchMode = false } = {}) {
42 var _a, _b, _c;
43 const data = require(path.join(packagePath, 'package.json'));
44 const ajv = new ajv_1.default({ useDefaults: true, strict: false });
45 const validate = ajv.compile(require('../metadata_schema.json'));
46 let valid = validate((_a = data.jupyterlab) !== null && _a !== void 0 ? _a : {});
47 if (!valid) {
48 console.error(validate.errors);
49 process.exit(1);
50 }
51 const outputPath = path.join(packagePath, data.jupyterlab['outputDir']);
52 const staticPath = path.join(outputPath, 'static');
53 // Handle the extension entry point and the lib entry point, if different
54 const index = require.resolve(packagePath);
55 const exposes = {
56 './index': index
57 };
58 const extension = data.jupyterlab.extension;
59 if (extension === true) {
60 exposes['./extension'] = index;
61 }
62 else if (typeof extension === 'string') {
63 exposes['./extension'] = path.join(packagePath, extension);
64 }
65 const mimeExtension = data.jupyterlab.mimeExtension;
66 if (mimeExtension === true) {
67 exposes['./mimeExtension'] = index;
68 }
69 else if (typeof mimeExtension === 'string') {
70 exposes['./mimeExtension'] = path.join(packagePath, mimeExtension);
71 }
72 if (typeof data.styleModule === 'string') {
73 exposes['./style'] = path.join(packagePath, data.styleModule);
74 }
75 else if (typeof data.style === 'string') {
76 exposes['./style'] = path.join(packagePath, data.style);
77 }
78 const coreData = require(path.join(corePath, 'package.json'));
79 let shared = {};
80 // Start with core package versions.
81 const coreDeps = {
82 ...coreData.dependencies,
83 ...((_b = coreData.resolutions) !== null && _b !== void 0 ? _b : {})
84 };
85 // Alow extensions to match a wider range than the core dependency
86 // To ensure forward compatibility.
87 Object.keys(coreDeps).forEach(element => {
88 shared[element] = {
89 requiredVersion: coreDeps[element].replace('~', '^'),
90 import: false
91 };
92 });
93 // Add package dependencies.
94 Object.keys(data.dependencies).forEach(element => {
95 // TODO: make sure that the core dependency semver range is a subset of our
96 // data.depencies version range for any packages in the core deps.
97 if (!shared[element]) {
98 shared[element] = {};
99 }
100 });
101 // Set core packages as singletons that are not bundled.
102 coreData.jupyterlab.singletonPackages.forEach((element) => {
103 if (!shared[element]) {
104 shared[element] = {};
105 }
106 shared[element].import = false;
107 shared[element].singleton = true;
108 });
109 // Now we merge in the sharedPackages configuration provided by the extension.
110 const sharedPackages = (_c = data.jupyterlab.sharedPackages) !== null && _c !== void 0 ? _c : {};
111 // Delete any modules that are explicitly not shared
112 Object.keys(sharedPackages).forEach(pkg => {
113 if (sharedPackages[pkg] === false) {
114 delete shared[pkg];
115 delete sharedPackages[pkg];
116 }
117 });
118 // Transform the sharedPackages information into valid webpack config
119 Object.keys(sharedPackages).forEach(pkg => {
120 var _a;
121 // Convert `bundled` to `import`
122 if (sharedPackages[pkg].bundled === false) {
123 sharedPackages[pkg].import = false;
124 }
125 else if (sharedPackages[pkg].bundled === true &&
126 ((_a = shared[pkg]) === null || _a === void 0 ? void 0 : _a.import) === false) {
127 // We can't delete a key in the merge, so we have to delete it in the source
128 delete shared[pkg].import;
129 }
130 delete sharedPackages[pkg].bundled;
131 });
132 shared = (0, webpack_merge_1.merge)(shared, sharedPackages);
133 // add the root module itself to shared
134 if (shared[data.name]) {
135 console.error(`The root package itself '${data.name}' may not specified as a shared dependency.`);
136 }
137 shared[data.name] = {
138 version: data.version,
139 singleton: true,
140 import: index
141 };
142 // Ensure a clean output directory - remove files but not the directory
143 // in case it is a symlink
144 fs.emptyDirSync(outputPath);
145 const extras = build_1.Build.ensureAssets({
146 packageNames: [],
147 packagePaths: [packagePath],
148 output: staticPath,
149 schemaOutput: outputPath,
150 themeOutput: outputPath
151 });
152 fs.copyFileSync(path.join(packagePath, 'package.json'), path.join(outputPath, 'package.json'));
153 class CleanupPlugin {
154 apply(compiler) {
155 compiler.hooks.done.tap('Cleanup', (stats) => {
156 const newlyCreatedAssets = stats.compilation.assets;
157 // Clear out any remoteEntry files that are stale
158 // https://stackoverflow.com/a/40370750
159 const files = glob.sync(path.join(staticPath, 'remoteEntry.*.js'));
160 let newEntry = '';
161 const unlinked = [];
162 files.forEach(file => {
163 const fileName = path.basename(file);
164 if (!newlyCreatedAssets[fileName]) {
165 fs.unlinkSync(path.resolve(file));
166 unlinked.push(fileName);
167 }
168 else {
169 newEntry = fileName;
170 }
171 });
172 if (unlinked.length > 0) {
173 console.log('Removed old assets: ', unlinked);
174 }
175 // Find the remoteEntry file and add it to the package.json metadata
176 const data = fs.readJSONSync(path.join(outputPath, 'package.json'));
177 const _build = {
178 load: path.join('static', newEntry)
179 };
180 if (exposes['./extension'] !== undefined) {
181 _build.extension = './extension';
182 }
183 if (exposes['./mimeExtension'] !== undefined) {
184 _build.mimeExtension = './mimeExtension';
185 }
186 if (exposes['./style'] !== undefined) {
187 _build.style = './style';
188 }
189 data.jupyterlab._build = _build;
190 fs.writeJSONSync(path.join(outputPath, 'package.json'), data, {
191 spaces: 2
192 });
193 });
194 }
195 }
196 // Allow custom webpack config
197 let webpackConfigPath = data.jupyterlab['webpackConfig'];
198 let webpackConfig = {};
199 // Use the custom webpack config only if the path to the config
200 // is specified in package.json (opt-in)
201 if (webpackConfigPath) {
202 webpackConfigPath = path.join(packagePath, webpackConfigPath);
203 if (fs.existsSync(webpackConfigPath)) {
204 webpackConfig = require(webpackConfigPath);
205 }
206 }
207 let plugins = [
208 new ModuleFederationPlugin({
209 name: data.name,
210 library: {
211 type: 'var',
212 name: ['_JUPYTERLAB', data.name]
213 },
214 filename: 'remoteEntry.[contenthash].js',
215 exposes,
216 shared
217 }),
218 new CleanupPlugin()
219 ];
220 if (mode === 'production') {
221 plugins.push(new webpack_plugins_1.WPPlugin.JSONLicenseWebpackPlugin({
222 excludedPackageTest: packageName => packageName === data.name
223 }));
224 }
225 // Add version argument when in production so the Jupyter server
226 // allows caching of files (i.e., does not set the CacheControl header to no-cache to prevent caching static files)
227 let filename = '[name].[contenthash].js';
228 if (mode === 'production') {
229 filename += '?v=[contenthash]';
230 }
231 const rules = [{ test: /\.html$/, type: 'asset/resource' }];
232 if (mode === 'development') {
233 rules.push({
234 test: /\.js$/,
235 enforce: 'pre',
236 use: ['source-map-loader']
237 });
238 }
239 const config = [
240 (0, webpack_merge_1.merge)(baseConfig, {
241 mode,
242 devtool,
243 entry: {},
244 output: {
245 filename,
246 path: staticPath,
247 publicPath: staticUrl || 'auto'
248 },
249 plugins
250 }, webpackConfig, {
251 module: {
252 rules
253 }
254 })
255 ].concat(extras);
256 if (mode === 'development') {
257 const logPath = path.join(outputPath, 'build_log.json');
258 function regExpReplacer(key, value) {
259 if (value instanceof RegExp) {
260 return value.toString();
261 }
262 else {
263 return value;
264 }
265 }
266 fs.writeFileSync(logPath, JSON.stringify(config, regExpReplacer, ' '));
267 }
268 return config;
269}
270exports.default = generateConfig;
271//# sourceMappingURL=extensionConfig.js.map
\No newline at end of file