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/lib/AppContext.js b/lib/AppContext.js index bb5bf991..e6182bfe 100644 --- a/lib/AppContext.js +++ b/lib/AppContext.js @@ -1,16 +1,17 @@ import * as $ from 'jquery'; import i18n from 'i18next'; -import { DataHarmonizer } from '@/lib'; -import { findLocalesForLangcodes } from '@/lib/utils/i18n'; +import { DataHarmonizer } from '.'; +import { findLocalesForLangcodes } from './utils/i18n'; import { Template, findSlotNamesForClass, getTemplatePathInScope, -} from '@/lib/utils/templates'; -import { wait } from '@/lib/utils/general'; -import { invert, removeNumericKeys, consolidate } from '@/lib/utils/objects'; -import { createDataHarmonizerContainer, createDataHarmonizerTab } from '@/web'; +} from '../lib/utils/templates'; +import { wait } from '../lib/utils/general'; +import { invert, removeNumericKeys, consolidate } from '../lib/utils/objects'; +import { createDataHarmonizerContainer, createDataHarmonizerTab } from '../web'; +import { getExportFormats } from 'schemas'; // COMMENTED OUT: WIP FEATURE // import { buildAppContext, setup1M } from '@/lib/utils/1m'; @@ -409,9 +410,7 @@ export default class AppContext { } async loadExportFormats(schema) { - this.exportFormats = ( - await import(`@/web/templates/${schema}/export.js`) - ).default; + this.exportFormats = await getExportFormats(schema); return this.exportFormats; } diff --git a/lib/DataHarmonizer.js b/lib/DataHarmonizer.js index 81c3130d..2e8306e6 100644 --- a/lib/DataHarmonizer.js +++ b/lib/DataHarmonizer.js @@ -7,9 +7,9 @@ import 'jquery-ui/dist/themes/base/jquery-ui.css'; import i18next from 'i18next'; import { utils as XlsxUtils, read as xlsxRead } from 'xlsx/xlsx.js'; -import { renderContent, urlToClickableAnchor } from '@/lib/utils/content'; -import { readFileAsync, updateSheetRange } from '@/lib/utils/files'; -import { findSlotNamesForClass } from '@/lib/utils/templates'; +import { renderContent, urlToClickableAnchor } from './utils/content'; +import { readFileAsync, updateSheetRange } from '../lib/utils/files'; +import { findSlotNamesForClass } from '../lib/utils/templates'; import { isValidHeaderRow, rowIsEmpty, @@ -17,8 +17,8 @@ import { stripDiv, isEmptyUnitVal, pascalToLowerWithSpaces, -} from '@/lib/utils/general'; -import { invert, deepMerge, looseMatchInObject } from '@/lib/utils/objects'; +} from '../lib/utils/general'; +import { invert, deepMerge, looseMatchInObject } from '../lib/utils/objects'; import { changeCase, @@ -31,7 +31,7 @@ import { MULTIVALUED_DELIMITER, parseMultivaluedValue, titleOverText, -} from '@/lib/utils/fields'; +} from './utils/fields'; import { checkProvenance, @@ -39,32 +39,34 @@ import { validateValAgainstVocab, validateValsAgainstVocab, // validateUniqueValues, -} from '@/lib/utils/validation'; +} from './utils/validation'; import 'handsontable/dist/handsontable.full.css'; -import '@/lib/data-harmonizer.css'; +import './data-harmonizer.css'; import '@selectize/selectize/dist/css/selectize.bootstrap4.css'; -import specifyHeadersModal from '@/lib/specifyHeadersModal.html'; -import unmappedHeadersModal from '@/lib/unmappedHeadersModal.html'; -import fieldDescriptionsModal from '@/lib/fieldDescriptionsModal.html'; +import specifyHeadersModal from './specifyHeadersModal.html'; +import unmappedHeadersModal from './unmappedHeadersModal.html'; +import fieldDescriptionsModal from './fieldDescriptionsModal.html'; -import HelpSidebar from '@/lib/HelpSidebar'; +import HelpSidebar from './HelpSidebar'; // NOTE: this is odd! package.json is a developer file. why should a UI component care about it? -import pkg from '@/package.json'; -import Validator from '@/lib/Validator'; +import pkg from '../package.json'; +import Validator from './Validator'; const VERSION = pkg.version; const VERSION_TEXT = 'DataHarmonizer v' + VERSION; -import { DateEditor, DatetimeEditor, TimeEditor } from '@/lib/editors'; import { + DateEditor, + DatetimeEditor, + TimeEditor, KeyValueListEditor, keyValueListValidator, keyValueListRenderer, multiKeyValueListRenderer, -} from '@/lib/editors'; +} from './editors'; Handsontable.cellTypes.registerCellType('key-value-list', { editor: KeyValueListEditor, @@ -2236,7 +2238,7 @@ class DataHarmonizer { // if sources exist, fetch transformed Value if (field.sources) { if (field.multivalued === true) { - //Map list of semicolon-delimited choices + // Map list of semicolon-delimited choices // ISSUE: relying on semicolon delimiter in input for (let cellVal of mappedCellVal.split(';')) { mappedCell.push( @@ -2655,7 +2657,6 @@ class DataHarmonizer { if (number >= 0) { // Here we have the 3 field call, with units sandwitched in the middle if (binOffset === 2) { - const unit = matrix[row][hotRowNextCol]; // Host age unit is interpreted by default to be year. // If user selects month, value is converted into years for binning. @@ -2693,7 +2694,7 @@ class DataHarmonizer { const bin_values = fields[hotRowBinCol].flatVocabulary; if (value in bin_values && (!bin_value || bin_value === '')) { selection = value; - console.log("no bin value", value); + console.log('no bin value', value); } // If a unit field exists, then set that to metadata too. if (binOffset == 2) { @@ -2742,7 +2743,7 @@ class DataHarmonizer { doPreValidationRepairs(data) { return new Promise((resolve) => { const cellChanges = []; - const whitespace_minimized_re = new RegExp(/\s+/,'g'); + const whitespace_minimized_re = new RegExp(/\s+/, 'g'); let fullVersion = VERSION_TEXT + ', ' + @@ -2762,15 +2763,16 @@ class DataHarmonizer { const datatype = field.datatype; if (cellVal && datatype === 'xsd:token') { - - const minimized = cellVal.replace(whitespace_minimized_re, ' ').trim(); + const minimized = cellVal + .replace(whitespace_minimized_re, ' ') + .trim(); // Update cellVal in advance of validateVal(s) below if (minimized !== cellVal) { cellVal = minimized; data[row][col] = cellVal; cellChanges.push([row, col, minimized, 'thisChange']); } - }; + } if (datatype === 'Provenance') { checkProvenance(cellChanges, fullVersion, cellVal, row, col); diff --git a/lib/HelpSidebar.js b/lib/HelpSidebar.js index 5feab2d1..f16dd730 100644 --- a/lib/HelpSidebar.js +++ b/lib/HelpSidebar.js @@ -1,6 +1,6 @@ import $ from 'jquery'; -import { renderContent } from '@/lib/utils/content'; -import '@/lib/HelpSidebar.css'; +import { renderContent } from './utils/content'; +import './HelpSidebar.css'; const DEFAULT_OPTIONS = { width: 300, diff --git a/lib/Toolbar.js b/lib/Toolbar.js index 7dc3f6d9..b69031f2 100644 --- a/lib/Toolbar.js +++ b/lib/Toolbar.js @@ -13,27 +13,24 @@ import { exportWorkbook, importJsonFile, prependToSheet, -} from '@/lib/utils/files'; -import { nullValuesToString, isEmptyUnitVal } from '@/lib/utils/general'; -import { MULTIVALUED_DELIMITER, titleOverText } from '@/lib/utils/fields'; -import { takeKeys, invert } from '@/lib/utils/objects'; +} from '../lib/utils/files'; +import { nullValuesToString, isEmptyUnitVal } from '../lib/utils/general'; +import { MULTIVALUED_DELIMITER, titleOverText } from '../lib/utils/fields'; +import { takeKeys, invert } from '../lib/utils/objects'; import { findBestLocaleMatch, templatePathForSchemaURI, rangeToContainerClass, LocaleNotSupportedError, -} from '@/lib/utils/templates'; +} from '../lib/utils/templates'; -import { - findLocalesForLangcodes, - interface_translation, -} from '@/lib/utils/i18n'; +import { findLocalesForLangcodes, interface_translation } from './utils/i18n'; import i18next from 'i18next'; -import template from '@/lib/toolbar.html'; -import '@/lib//toolbar.css'; +import template from './toolbar.html'; +import './/toolbar.css'; -import menu from '@/web/templates/menu.json'; +import { menu } from 'schemas'; import pkg from '../package.json'; const VERSION = pkg.version; diff --git a/lib/Validator.js b/lib/Validator.js index 248c91d9..1b61650e 100644 --- a/lib/Validator.js +++ b/lib/Validator.js @@ -1,6 +1,6 @@ -import { Datatypes } from '@/lib/utils/datatypes'; -import { validateUniqueValues } from '@/lib/utils/validation'; -import { rowIsEmpty } from '@/lib/utils/general'; +import { Datatypes } from './utils/datatypes'; +import { validateUniqueValues } from './utils/validation'; +import { rowIsEmpty } from './utils/general'; class Validator { #schema; @@ -58,7 +58,7 @@ class Validator { return; } // todos is an array of strings. - const slotType = this.getSlotType(slotDefinition);// LinkML schema.type object + const slotType = this.getSlotType(slotDefinition); // LinkML schema.type object // Slot type could be: // a number, string, date ... @@ -74,11 +74,15 @@ class Validator { } } - // Cycle through each slotDefinition any_of etc. object entries and get + // Cycle through each slotDefinition any_of etc. object entries and get // the datatype of its .range (or recurse) and in LinkML fashion attach // minimum_value and maximum_value to the slotDefinition OR its any_of // etc array BASED ON top level todos. E.g. inheriting min/max criteria. - for (const def of slotDefinition.any_of ?? slotDefinition.all_of ?? slotDefinition.exactly_one_of ?? slotDefinition.none_of ?? []) { + for (const def of slotDefinition.any_of ?? + slotDefinition.all_of ?? + slotDefinition.exactly_one_of ?? + slotDefinition.none_of ?? + []) { processTodos(def, todos); } }; @@ -248,8 +252,7 @@ class Validator { const validate = (value) => { if (!value) { - if (slotDefinition.required) - return 'This field is required'; + if (slotDefinition.required) return 'This field is required'; // value_presence is subject to dynamic rules? if (slotDefinition.value_presence === 'PRESENT') return 'Value is not present'; @@ -283,7 +286,8 @@ class Validator { // Message needs for (const value of splitValues) { let parse_error = false; - if (slotType) {// Doesn't pertain to slots which are ONLY enumerations. + if (slotType) { + // Doesn't pertain to slots which are ONLY enumerations. const parsed = this.#parser.parse(value, slotType.uri); // Issue: parse can fail on decimal but menu has "Missing" @@ -297,15 +301,14 @@ class Validator { // All these cases have encountered an item which matches basic data // datatype and so sudden death is ok. else { - if (slotMinimumValue !== undefined && parsed < slotMinimumValue) { return 'Value is less than minimum value'; } - + if (slotMaximumValue !== undefined && parsed > slotMaximumValue) { return 'Value is greater than maximum value'; } - + if ( (slotDefinition.equals_string !== undefined && parsed !== slotDefinition.equals_string) || @@ -322,14 +325,12 @@ class Validator { return 'Value does not match pattern'; } - // Here slotType value tested and is ok! + // Here slotType value tested and is ok! continue; } // Here value didn't parse to slotType - - } - else { + } else { // No basic slot type here so only enumeration handling. } @@ -378,17 +379,20 @@ class Validator { } } - if (anyOfValidators.length || allOfValidators.length || exactlyOneOfValidators.length || noneOfValidators.length) { + if ( + anyOfValidators.length || + allOfValidators.length || + exactlyOneOfValidators.length || + noneOfValidators.length + ) { // We passed validation here which means a parse error can be overriden - } - else if (parse_error.length) { - //There were no other ranges besides basic slotType so + } else if (parse_error.length) { + //There were no other ranges besides basic slotType so return parse_error; } } }; - return validate; } diff --git a/lib/editors/DateEditor.js b/lib/editors/DateEditor.js index 8beb6b7c..6d50aa1d 100644 --- a/lib/editors/DateEditor.js +++ b/lib/editors/DateEditor.js @@ -1,4 +1,4 @@ -import FlatpickrEditor from '@/lib/editors/FlatpickrEditor'; +import FlatpickrEditor from './FlatpickrEditor'; class DateEditor extends FlatpickrEditor { getFlatpickrConfig() { diff --git a/lib/editors/DatetimeEditor.js b/lib/editors/DatetimeEditor.js index c0f82642..6755272e 100644 --- a/lib/editors/DatetimeEditor.js +++ b/lib/editors/DatetimeEditor.js @@ -1,4 +1,4 @@ -import FlatpickrEditor from '@/lib/editors/FlatpickrEditor'; +import FlatpickrEditor from './FlatpickrEditor'; class DatetimeEditor extends FlatpickrEditor { getFlatpickrConfig() { diff --git a/lib/editors/KeyValueEditor.js b/lib/editors/KeyValueEditor.js index 745bf1a3..f38fc1f1 100644 --- a/lib/editors/KeyValueEditor.js +++ b/lib/editors/KeyValueEditor.js @@ -1,6 +1,6 @@ import Handsontable from 'handsontable'; -import { MULTIVALUED_DELIMITER, titleOverText } from '@/lib/utils/fields'; -import { isEmptyUnitVal } from '@/lib/utils/general'; +import { MULTIVALUED_DELIMITER, titleOverText } from '../utils/fields'; +import { isEmptyUnitVal } from '../utils/general'; // Derived from: https://jsfiddle.net/handsoncode/f0b41jug/ diff --git a/lib/editors/TimeEditor.js b/lib/editors/TimeEditor.js index db482459..81b43096 100644 --- a/lib/editors/TimeEditor.js +++ b/lib/editors/TimeEditor.js @@ -1,4 +1,4 @@ -import FlatpickrEditor from '@/lib/editors/FlatpickrEditor'; +import FlatpickrEditor from './FlatpickrEditor'; class TimeEditor extends FlatpickrEditor { getFlatpickrConfig() { diff --git a/lib/editors/index.js b/lib/editors/index.js index a8893c79..ffda2949 100644 --- a/lib/editors/index.js +++ b/lib/editors/index.js @@ -1,10 +1,10 @@ -export { default as DateEditor } from '@/lib/editors/DateEditor'; -export { default as DatetimeEditor } from '@/lib/editors/DatetimeEditor'; -export { default as TimeEditor } from '@/lib/editors/TimeEditor'; -export { default as FlatpickrEditor } from '@/lib/editors/FlatpickrEditor'; +export { default as DateEditor } from './DateEditor.js'; +export { default as DatetimeEditor } from './DatetimeEditor.js'; +export { default as TimeEditor } from './TimeEditor.js'; +export { default as FlatpickrEditor } from './FlatpickrEditor.js'; export { default as KeyValueListEditor, keyValueListRenderer, keyValueListValidator, multiKeyValueListRenderer, -} from '@/lib/editors/KeyValueEditor'; +} from './KeyValueEditor.js'; diff --git a/lib/index.js b/lib/index.js index c33acea6..367f7ecb 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,9 +1,5 @@ -export { default as Footer } from '@/lib/Footer'; -export { default as Toolbar } from '@/lib/Toolbar'; -export { default as DataHarmonizer } from '@/lib/DataHarmonizer'; -export { default as AppContext } from '@/lib/AppContext'; -export { - DATE_OBJECT, - JSON_SCHEMA_FORMAT, - INPUT_FORMAT, -} from '@/lib/utils/fields'; +export { default as Footer } from './Footer'; +export { default as Toolbar } from './Toolbar'; +export { default as DataHarmonizer } from './DataHarmonizer'; +export { default as AppContext } from './AppContext'; +export { DATE_OBJECT, JSON_SCHEMA_FORMAT, INPUT_FORMAT } from './utils/fields'; diff --git a/lib/rollup.config.js b/lib/rollup.config.js index 4620479c..3cd72bd4 100644 --- a/lib/rollup.config.js +++ b/lib/rollup.config.js @@ -37,8 +37,8 @@ export default { image(), alias({ entries: [ - // Replace '@/lib' with the actual path to the 'lib' directory - { find: '@/lib', replacement: path.resolve(__dirname, '.') }, + // Replace '.' with the actual path to the 'lib' directory + { find: '.', replacement: path.resolve(__dirname, '.') }, ], }), string({ diff --git a/lib/toolbarGettingStarted.js b/lib/toolbarGettingStarted.js index 7e12c4d9..2ab7e254 100644 --- a/lib/toolbarGettingStarted.js +++ b/lib/toolbarGettingStarted.js @@ -1,19 +1,19 @@ -import editCopyPasteDelete from '@/lib/images/editCopyPasteDelete.gif'; -import changeTemplate from '@/lib/images/changeTemplate.gif'; -import toggleRequiredCols from '@/lib/images/toggleRequiredCols.gif'; -import doubleClickHeaders from '@/lib/images/doubleClickHeaders.gif'; -import selectingVals from '@/lib/images/selectingVals.gif'; -import validatingCells from '@/lib/images/validatingCells.gif'; -import showRows from '@/lib/images/showRows.gif'; -import showSection from '@/lib/images/showSection.gif'; -import jumpToColumn from '@/lib/images/jumpToColumn.gif'; -import fillColumn from '@/lib/images/fillColumn.gif'; -import exportingFiles from '@/lib/images/exportingFiles.gif'; -import provenance from '@/lib/images/provenance.gif'; -import moreInfo from '@/lib/images/moreInfo.gif'; -import versionUpdate from '@/lib/images/versionUpdate.gif'; +import editCopyPasteDelete from './images/editCopyPasteDelete.gif'; +import changeTemplate from './images/changeTemplate.gif'; +import toggleRequiredCols from './images/toggleRequiredCols.gif'; +import doubleClickHeaders from './images/doubleClickHeaders.gif'; +import selectingVals from './images/selectingVals.gif'; +import validatingCells from './images/validatingCells.gif'; +import showRows from './images/showRows.gif'; +import showSection from './images/showSection.gif'; +import jumpToColumn from './images/jumpToColumn.gif'; +import fillColumn from './images/fillColumn.gif'; +import exportingFiles from './images/exportingFiles.gif'; +import provenance from './images/provenance.gif'; +import moreInfo from './images/moreInfo.gif'; +import versionUpdate from './images/versionUpdate.gif'; -import { renderContent } from '@/lib/utils/content'; +import { renderContent } from '../lib/utils/content'; import i18next from 'i18next'; const slides = [ diff --git a/lib/utils/1m.js b/lib/utils/1m.js index 41bf249a..c0f2935d 100644 --- a/lib/utils/1m.js +++ b/lib/utils/1m.js @@ -225,8 +225,8 @@ On load of a schema the appContext object is filled in, and event triggers are c */ // Example: -// import grdi_json from '@/web/templates/grdi/schema.json'; -// import schema_editor_json from '@/web/templates/schema_editor/schema.json'; +// import grdi_json from '../templates/grdi/schema.json'; +// import schema_editor_json from '../templates/schema_editor/schema.json'; // const AppContext = buildAppContext(grdi_json); // const AppContext2 = buildAppContext(schema_editor_json); diff --git a/lib/utils/content.js b/lib/utils/content.js index 955f21d9..ce8e5ab6 100644 --- a/lib/utils/content.js +++ b/lib/utils/content.js @@ -1,6 +1,6 @@ import MarkdownIt from 'markdown-it'; import linkifyIt from 'linkify-it'; -import { formatEscapeHTML } from '@/lib/utils/fields'; +import { formatEscapeHTML } from './fields'; const linkify = linkifyIt(); const md = new MarkdownIt({ diff --git a/lib/utils/datatypes.js b/lib/utils/datatypes.js index 2483e9ae..18cf2cbf 100644 --- a/lib/utils/datatypes.js +++ b/lib/utils/datatypes.js @@ -6,7 +6,7 @@ const DEFAULT_OPTIONS = { datetimeFormat: 'yyyy-MM-dd HH:mm', timeFormat: 'HH:mm', }; -const RE_WHITESPACE_MINIMIZED = new RegExp(/\s+/,'g'); +const RE_WHITESPACE_MINIMIZED = new RegExp(/\s+/, 'g'); const RE_DECIMAL = new RegExp(/^[+-]?([0-9]+(\.[0-9]*)?|\.[0-9]+)$/); export class Datatypes { diff --git a/lib/utils/fields.js b/lib/utils/fields.js index 536e1d51..94626733 100644 --- a/lib/utils/fields.js +++ b/lib/utils/fields.js @@ -5,8 +5,8 @@ import { parseJsonSchemaDate, parseJsonDate, datatypeIsDateOrTime, -} from '@/lib/utils/datatypes'; -import { consolidate } from '@/lib/utils/objects'; +} from './datatypes.js'; +import { consolidate } from './objects.js'; export const MULTIVALUED_DELIMITER = '; '; diff --git a/lib/utils/general.js b/lib/utils/general.js index 595d9f7b..836bc534 100644 --- a/lib/utils/general.js +++ b/lib/utils/general.js @@ -1,4 +1,4 @@ -import { consolidate } from '@/lib/utils/objects'; +import { consolidate } from './objects'; export function wait(ms) { return new Promise((r) => setTimeout(r, ms)); diff --git a/lib/utils/i18n.js b/lib/utils/i18n.js index 52d29c45..05a2fa8b 100644 --- a/lib/utils/i18n.js +++ b/lib/utils/i18n.js @@ -3,13 +3,10 @@ import i18next from 'i18next'; import jqueryI18next from 'jquery-i18next'; // import tags from 'language-tags'; -import { flattenObject } from '@/lib/utils/objects'; -import label_translations_file from '@/web/translations/translations.json'; -import { - MULTIVALUED_DELIMITER, - formatMultivaluedValue, -} from '@/lib/utils/fields'; -import { renderContent } from '@/lib/utils/content'; +import { flattenObject } from './objects'; +import label_translations_file from '../../web/translations/translations.json'; +import { MULTIVALUED_DELIMITER, formatMultivaluedValue } from './fields'; +import { renderContent } from './content'; export const interface_translation = transformStructFirstSpec( label_translations_file diff --git a/lib/utils/templates.js b/lib/utils/templates.js index a2bd3831..abb686a3 100644 --- a/lib/utils/templates.js +++ b/lib/utils/templates.js @@ -1,8 +1,8 @@ -import { deepMerge } from '@/lib/utils/objects'; -import { fetchFileAsync } from '@/lib/utils/files'; +import { deepMerge } from './objects'; +import { fetchFileAsync } from './files'; -import template_manifest from '@/web/templates/manifest.json'; -import menu from '@/web/templates/menu.json'; +import template_manifest from '../../web/templates/manifest.json'; +import { menu, getSchema } from 'schemas'; export function getTemplatePathInScope() { let templatePath; @@ -25,8 +25,12 @@ export async function templatePathForSchemaURI(schemaURI) { // for now, this is just the manifest for (let i = 0; i < template_manifest.children.length; i++) { const template = template_manifest.children[i]; - if (typeof template.children !== 'undefined' && template.children.length > 0 && template.children.some(c => c.name.includes("schema.json"))) { - const schema = await importSchema(template.name); + if ( + typeof template.children !== 'undefined' && + template.children.length > 0 && + template.children.some((c) => c.name.includes('schema.json')) + ) { + const schema = await getSchema(template.name); if (schema.id === schemaURI) { const templatePath = `${template.path.split('/').slice(-1)}/${schemaURI .split('/') @@ -47,14 +51,8 @@ const isLocale = (el) => el.name === 'locales'; const schemaFromChildPath = (childPath) => childPath.replace(/\/templates\/(.+)\/schema.json/, '$1'); -async function importSchema(schema_name) { - const schema = ( - await import(`../../web/templates/${schema_name}/schema.json`) - ).default; - return schema; -} const importSchemaFromChildPath = (childPath) => - importSchema(schemaFromChildPath(childPath)); + getSchema(schemaFromChildPath(childPath)); const templateFetcher = async (childPath) => { if (window.location.protocol.startsWith('file')) { diff --git a/lib/utils/validation.js b/lib/utils/validation.js index 06e7aade..8108b81d 100644 --- a/lib/utils/validation.js +++ b/lib/utils/validation.js @@ -1,7 +1,4 @@ -import { - formatMultivaluedValue, - parseMultivaluedValue, -} from '@/lib/utils/fields'; +import { formatMultivaluedValue, parseMultivaluedValue } from './fields'; /** * Test cellVal against "DataHarmonizer provenance: vX.Y.Z" pattern and if it @@ -82,7 +79,7 @@ export function validateValAgainstVocab(value, field) { const ptr = field.flatVocabularyLCase.indexOf(trimmedVal); if (ptr >= 0) { valid = true; - // Normalised value being suggested for update; + // Normalised value being suggested for update; // .trim() because flatVocabulary has indentation. let val_trim = field.flatVocabulary[ptr].trim(); if (value != val_trim) { diff --git a/package.json b/package.json index c5471e46..f9b4a9e9 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", "flightcheck": "yarn format && yarn lint ; yarn test", "test": "jest tests/" diff --git a/web/index.css b/web/index.css index 1b913b13..17b469af 100644 --- a/web/index.css +++ b/web/index.css @@ -9,7 +9,7 @@ body { /* For dh2-i18n-rc1 only! */ #data-harmonizer-tabs { - display: none; + /* display: none; */ } .data-harmonizer-grid .secondary-header-cell:hover { diff --git a/web/index.html b/web/index.html index 518044ca..51c0bd2b 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 6a8774d8..0f2e9ee5 100644 --- a/web/index.js +++ b/web/index.js @@ -4,13 +4,13 @@ import * as $ from 'jquery'; import 'bootstrap/js/dist/tab'; import 'bootstrap/dist/css/bootstrap.min.css'; -import { initI18n } from '@/lib/utils/i18n'; -import { Template } from '@/lib/utils/templates'; -import { getGettingStartedMarkup } from '@/lib/toolbarGettingStarted'; -import { Footer, Toolbar, AppContext } from '@/lib'; +import { initI18n } from '../lib/utils/i18n'; +import { Template } from '../lib/utils/templates'; +import { getGettingStartedMarkup } from '../lib/toolbarGettingStarted'; +import { Footer, Toolbar, AppContext } from '../lib'; // Order matters: place this at bottom of imports for CSS overrides -import '@/web/index.css'; +import './index.css'; const dhRoot = document.querySelector('#data-harmonizer-grid'); const dhFooterRoot = document.querySelector('#data-harmonizer-footer'); 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 03de4ec6..a2201df0 100644 --- a/web/webpack.config.js +++ b/web/webpack.config.js @@ -16,7 +16,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: [ // necessary for templates.js @@ -33,11 +42,6 @@ module.exports = (env, argv) => { }), new CopyPlugin({ patterns: [ - { - context: 'templates', - from: '**/*.pdf', - to: 'templates/[path][name][ext]', - }, { context: 'templates', from: '**/schema.json', @@ -51,10 +55,6 @@ module.exports = (env, argv) => { ], module: { rules: [ - { - test: /\.ya?ml$/, - use: 'yaml-loader', - }, { test: /\.xlsx$/, loader: 'webpack-xlsx-loader' }, { test: /\.(c|d|t)sv$/, // load all .csv, .dsv, .tsv files with dsv-loader @@ -72,6 +72,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, + }, }, ], }, @@ -81,8 +87,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]', + }, + ], + }), + ], +};