diff --git a/.eslintrc.js b/.eslintrc.js
index 2b2592de..64e5283d 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -2,6 +2,7 @@ module.exports = {
ignorePatterns: [
'.eslintrc.js',
'web/webpack.config.js',
+ 'web/webpack.schemas.js',
'lib/rollup.config.js',
'**/dist/**/*.js',
'.venv',
diff --git a/package.json b/package.json
index 7a0a2af3..2e38e4d7 100644
--- a/package.json
+++ b/package.json
@@ -27,9 +27,11 @@
"lint": "prettier --check . && eslint .",
"clean:lib": "rimraf lib/dist",
"clean:web": "rimraf web/dist",
+ "clean:schemas": "rimraf web/dist/dist-schemas web/dist/templates",
"clean": "yarn clean:lib && yarn clean:web",
"build:lib": "yarn clean:lib && rollup --config lib/rollup.config.js",
- "build:web": "yarn clean:web && webpack --mode=production --config web/webpack.config.js",
+ "build:web": "yarn clean:web && webpack --mode=production --config web/webpack.config.js && yarn build:schemas",
+ "build:schemas": "yarn clean:schemas && webpack --config web/webpack.schemas.js",
"dev": "webpack serve --mode=development --config web/webpack.config.js",
"test": "jest tests/"
},
diff --git a/web/index.html b/web/index.html
index 6d6dc3ad..78a1a14a 100644
--- a/web/index.html
+++ b/web/index.html
@@ -5,6 +5,8 @@
DataHarmonizer
+
+
diff --git a/web/index.js b/web/index.js
index 52925b25..df455311 100644
--- a/web/index.js
+++ b/web/index.js
@@ -1,9 +1,10 @@
import { DataHarmonizer, Footer, Toolbar } from '../lib';
-import menu from './templates/menu.json';
import 'bootstrap/dist/css/bootstrap.min.css';
import './index.css';
+import { menu, getSchema, getExportFormats } from 'schemas';
+
document.addEventListener('DOMContentLoaded', function () {
const dhRoot = document.querySelector('#data-harmonizer-grid');
const dhFooterRoot = document.querySelector('#data-harmonizer-footer');
@@ -25,11 +26,7 @@ document.addEventListener('DOMContentLoaded', function () {
new Toolbar(dhToolbarRoot, dh, menu, {
templatePath: templatePath,
releasesURL: 'https://github.com/cidgoh/pathogen-genomics-package/releases',
- getSchema: async (schema) => {
- return (await import(`./templates/${schema}/schema.json`)).default;
- },
- getExportFormats: async (schema) => {
- return (await import(`./templates/${schema}/export.js`)).default;
- },
+ getSchema: getSchema,
+ getExportFormats: getExportFormats,
});
});
diff --git a/web/schemas.js b/web/schemas.js
new file mode 100644
index 00000000..87282349
--- /dev/null
+++ b/web/schemas.js
@@ -0,0 +1,9 @@
+import menu_ from './templates/menu.json';
+
+export const menu = menu_;
+export const getSchema = async (schema) => {
+ return (await import(`./templates/${schema}/schema.json`)).default;
+};
+export const getExportFormats = async (schema) => {
+ return (await import(`./templates/${schema}/export.js`)).default;
+};
diff --git a/web/webpack.config.js b/web/webpack.config.js
index 5e6c2eb4..14201de3 100644
--- a/web/webpack.config.js
+++ b/web/webpack.config.js
@@ -9,7 +9,16 @@ module.exports = (env, argv) => {
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'scripts/[name].js',
- assetModuleFilename: 'assets/[hash][ext][query]',
+ },
+ externals: {
+ // Without declaring `schemas` as external, Webpack will attempt to look
+ // for the `schemas` library and bundle it. However, we want our schemas
+ // bundle to be separate from this one. This external config tells webpack
+ // that the schemas library will instead be supplied at runtime. External
+ // libraries can be provided in multiple ways, but we provide it through a
+ // script reference in the HTML file outputted by this bundle.
+ // https://webpack.js.org/configuration/externals/#externals
+ schemas: 'schemas',
},
plugins: [
new HtmlWebpackPlugin({
@@ -17,21 +26,6 @@ module.exports = (env, argv) => {
}),
new CopyPlugin({
patterns: [
- {
- context: 'templates',
- from: '**/*.pdf',
- to: 'templates/[path][name][ext]',
- },
- {
- context: 'templates',
- from: '**/schema.yaml',
- to: 'templates/[path][name][ext]',
- },
- {
- context: 'templates',
- from: '**/exampleInput/*',
- to: 'templates/[path][name][ext]',
- },
{
from: 'main.html',
},
@@ -52,6 +46,12 @@ module.exports = (env, argv) => {
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
+ generator: {
+ // Will throw a file not found error if ``dist`` folder not in
+ // ``{project root}/web/``.
+ filename: '../[file]',
+ emit: false,
+ },
},
],
},
@@ -61,8 +61,44 @@ module.exports = (env, argv) => {
},
};
+ // Difficult to run two webpack instances on a single dev server (i.e., this
+ // file, and the other webpack file that builds schemas). So for dev servers,
+ // we will stick to a singular build that concatenates both application and
+ // schema content. This is fine, because the whole point of having a separate
+ // build for schema content was to reduce production build times when users
+ // are only editing schema files (and not the rest of the application), but
+ // the dev server already gets around this problem through hot loading.
if (argv.mode === 'development') {
config.devtool = 'eval-source-map';
+ // The external schemas lib is replaced by a direct reference to the
+ // schemas entrypoint. When you directly reference the schemas entrypoint in
+ // this bundle, a separate schemas library is not needed, because this
+ // bundle will now include all schema content as well.
+ config.resolve = {
+ alias: {
+ schemas: path.resolve(__dirname, 'schemas.js'),
+ },
+ };
+ delete config.externals;
+ // Need pdf SOPs that schema build previously supplied
+ config.plugins.push(
+ new CopyPlugin({
+ patterns: [
+ {
+ context: 'templates',
+ from: '**/*.pdf',
+ to: 'templates/[path][name][ext]',
+ },
+ ],
+ })
+ );
+ // False emits don't play nice with dev servers either
+ for (const rule of config.module.rules) {
+ if (rule.hasOwnProperty('generator')) {
+ delete rule.generator.filename;
+ delete rule.generator.emit;
+ }
+ }
}
return config;
diff --git a/web/webpack.schemas.js b/web/webpack.schemas.js
new file mode 100644
index 00000000..81be76fa
--- /dev/null
+++ b/web/webpack.schemas.js
@@ -0,0 +1,29 @@
+const path = require('path');
+const CopyPlugin = require('copy-webpack-plugin');
+
+module.exports = {
+ context: path.resolve(__dirname),
+ entry: {
+ schemas: './schemas.js',
+ },
+ output: {
+ path: path.resolve(__dirname, 'dist', 'dist-schemas'),
+ filename: '[name].js',
+ globalObject: 'this',
+ library: {
+ name: 'schemas',
+ type: 'umd',
+ },
+ },
+ plugins: [
+ new CopyPlugin({
+ patterns: [
+ {
+ context: 'templates',
+ from: '**/*.pdf',
+ to: '../templates/[path][name][ext]',
+ },
+ ],
+ }),
+ ],
+};