mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Replace vulcanize process with loose files
It's a lot easier to debug, but may have reduced startup performance. Closes #682.
This commit is contained in:
+3
-2
@@ -524,9 +524,10 @@ add_custom_command(TARGET LOOT POST_BUILD
|
||||
$<TARGET_FILE_DIR:LOOT>/resources/ui/fonts/${font})
|
||||
ENDFOREACH()
|
||||
|
||||
# Run Vulcanize to build the UI HTML.
|
||||
# Build the UI HTML.
|
||||
add_custom_command(TARGET LOOT POST_BUILD
|
||||
COMMAND "node" "${CMAKE_SOURCE_DIR}/scripts/vulcanize.js" ${CMAKE_SOURCE_DIR})
|
||||
COMMAND "node" "${CMAKE_SOURCE_DIR}/scripts/build_ui.js"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
|
||||
# Copy testing metadata
|
||||
ExternalProject_Get_Property(testing-metadata SOURCE_DIR)
|
||||
|
||||
@@ -51,7 +51,7 @@ You may also need to set `BOOST_ROOT` if CMake cannot find Boost.
|
||||
|
||||
### Rebuilding the HTML UI
|
||||
|
||||
The GUI's HTML file is automatically built when building the LOOT GUI binary, but it can also be built by running `node scripts/vulcanize.js` from the repository root.
|
||||
The GUI's HTML file is automatically built when building the LOOT GUI binary, but it can also be built by running `node scripts/build_ui.js` from the repository root.
|
||||
|
||||
## Building The Documentation
|
||||
|
||||
|
||||
+2
-2
@@ -14,8 +14,8 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"fs-extra": "^0.30.0",
|
||||
"replace": "^0.3.0",
|
||||
"vulcanize": "^1.14.5"
|
||||
"hydrolysis": "^1.24.1",
|
||||
"replace": "^0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bower": "^1.7.1",
|
||||
|
||||
+149
-149
File diff suppressed because it is too large
Load Diff
+2
-11
@@ -140,18 +140,9 @@ function createAppArchive(rootPath, releasePath, tempPath, destPath) {
|
||||
});
|
||||
|
||||
// UI files.
|
||||
fs.mkdirsSync(path.join(tempPath, 'resources', 'ui', 'css'));
|
||||
fs.copySync(
|
||||
path.join(releasePath, 'resources', 'ui', 'index.html'),
|
||||
path.join(tempPath, 'resources', 'ui', 'index.html')
|
||||
);
|
||||
fs.copySync(
|
||||
path.join(rootPath, 'resources', 'ui', 'css', 'dark-theme.css'),
|
||||
path.join(tempPath, 'resources', 'ui', 'css', 'dark-theme.css')
|
||||
);
|
||||
fs.copySync(
|
||||
path.join(rootPath, 'resources', 'ui', 'fonts'),
|
||||
path.join(tempPath, 'resources', 'ui', 'fonts')
|
||||
path.join(releasePath, 'resources', 'ui'),
|
||||
path.join(tempPath, 'resources', 'ui')
|
||||
);
|
||||
|
||||
// Documentation.
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
'use strict';
|
||||
const helpers = require('./helpers');
|
||||
const hyd = require('hydrolysis');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
function getHtmlImports(filePath) {
|
||||
return hyd.Analyzer.analyze(filePath).then((analyzer) =>
|
||||
analyzer._getDependencies(filePath)
|
||||
);
|
||||
}
|
||||
|
||||
function isString(variable) {
|
||||
return typeof variable === 'string' || variable instanceof String;
|
||||
}
|
||||
|
||||
function flattenUnique(array) {
|
||||
const results = new Set();
|
||||
array.forEach((element) => {
|
||||
if (isString(element)) {
|
||||
results.add(element);
|
||||
} else if (element) {
|
||||
flattenUnique(element).forEach((subelement) => {
|
||||
results.add(subelement);
|
||||
});
|
||||
}
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
function getRecursiveHtmlImports(filePath, imports) {
|
||||
return getHtmlImports(filePath).then((paths) =>
|
||||
Promise.all(paths.map((dependency) => {
|
||||
if (imports.has(dependency)) {
|
||||
return null;
|
||||
}
|
||||
imports.add(dependency);
|
||||
return getRecursiveHtmlImports(dependency, imports);
|
||||
}))
|
||||
).then((results) => {
|
||||
flattenUnique(results).forEach((dependency) => {
|
||||
imports.add(dependency);
|
||||
});
|
||||
|
||||
return imports;
|
||||
});
|
||||
}
|
||||
|
||||
function getJavaScriptSources(filePath) {
|
||||
return hyd.Analyzer.analyze(filePath).then((analyzer) =>
|
||||
Object.keys(analyzer.parsedScripts).filter((script) =>
|
||||
script.endsWith('.js')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function getRelativePath(filePath) {
|
||||
if (filePath.startsWith('src/gui/html/')) {
|
||||
return filePath.substring(13);
|
||||
}
|
||||
return filePath;
|
||||
}
|
||||
|
||||
function normalisePaths(html) {
|
||||
return html.replace(/href="(\.\.\/){3}/g, 'href="')
|
||||
.replace(/src="(\.\.\/){3}/g, 'src="');
|
||||
}
|
||||
|
||||
function copyNormalisedFile(sourceFile, destinationFile) {
|
||||
const html = fs.readFileSync(sourceFile, { encoding: 'utf8' });
|
||||
fs.mkdirs(path.dirname(destinationFile));
|
||||
fs.writeFileSync(destinationFile, normalisePaths(html));
|
||||
}
|
||||
|
||||
function copyFiles(pathsPromise, destinationRootPath) {
|
||||
pathsPromise.then((paths) => {
|
||||
paths.forEach((filePath) => {
|
||||
const destinationPath = `${destinationRootPath}/${getRelativePath(filePath)}`;
|
||||
if (filePath.includes('bower_components')) {
|
||||
fs.copySync(filePath, destinationPath);
|
||||
} else {
|
||||
copyNormalisedFile(filePath, destinationPath);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
helpers.getAppReleasePaths('.').forEach(releasePath => {
|
||||
const index = 'src/gui/html/index.html';
|
||||
const destinationRootPath = `${releasePath.path}/resources/ui`;
|
||||
const imports = new Set();
|
||||
|
||||
copyFiles(getRecursiveHtmlImports(index, imports), destinationRootPath);
|
||||
copyFiles(getJavaScriptSources(index), destinationRootPath);
|
||||
fs.copySync('src/gui/html/css', `${destinationRootPath}/css`);
|
||||
fs.copySync('resources/ui/css/dark-theme.css', `${destinationRootPath}/css/dark-theme.css`);
|
||||
fs.copySync('resources/ui/fonts', `${destinationRootPath}/fonts`);
|
||||
copyNormalisedFile(index, `${destinationRootPath}/index.html`);
|
||||
|
||||
// This is the only JS file referenced by a HTML import (neon-animation),
|
||||
// so just hardcode it instead of recursively searching for it.
|
||||
const webAnimationsJs = 'bower_components/web-animations-js/web-animations-next-lite.min.js';
|
||||
fs.copySync(webAnimationsJs, `${destinationRootPath}/${webAnimationsJs}`);
|
||||
});
|
||||
@@ -105,12 +105,8 @@ DestDir: "{app}\resources\l10n"; Flags: ignoreversion
|
||||
Source: "{#buildir}\docs\html\*"; \
|
||||
DestDir: "{app}\docs"; Flags: ignoreversion recursesubdirs
|
||||
|
||||
Source: "{#buildir}\Release\resources\ui\index.html"; \
|
||||
DestDir: "{app}\resources\ui"; Flags: ignoreversion
|
||||
Source: "resources\ui\css\dark-theme.css"; \
|
||||
DestDir: "{app}\resources\ui\css"; Flags: ignoreversion
|
||||
Source: "resources\ui\fonts\*"; \
|
||||
DestDir: "{app}\resources\ui\fonts"; Flags: ignoreversion
|
||||
Source: "{#buildir}\Release\resources\ui\*"; \
|
||||
DestDir: "{app}\resources\ui"; Flags: ignoreversion recursesubdirs
|
||||
|
||||
Source: "resources\l10n\da\LC_MESSAGES\loot.mo"; \
|
||||
DestDir: "{app}\resources\l10n\da\LC_MESSAGES"; Flags: ignoreversion
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
// Build the UI's index.html file. Takes one argument, which is the path to the
|
||||
// repository's root.
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
'use strict';
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const helpers = require('./helpers');
|
||||
const Vulcanize = require('vulcanize');
|
||||
const mkdirp = require('mkdirp');
|
||||
|
||||
// Initialise from command line parameters.
|
||||
let rootPath = '.';
|
||||
let buildType = 'ui';
|
||||
if (process.argv.length > 3) {
|
||||
rootPath = process.argv[2];
|
||||
buildType = process.argv[3];
|
||||
} else if (process.argv.length > 2) {
|
||||
rootPath = process.argv[2];
|
||||
}
|
||||
|
||||
const vulcanize = new Vulcanize({
|
||||
inlineScripts: true,
|
||||
inlineCss: true,
|
||||
excludes: [
|
||||
'css/theme.css',
|
||||
],
|
||||
});
|
||||
|
||||
function mkdir(dir) {
|
||||
try {
|
||||
mkdirp.sync(dir);
|
||||
} catch (e) {
|
||||
if (e.code !== 'EEXIST') {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function vulcanizeFile(inputPath, outputPath) {
|
||||
// Make sure output directory exists first.
|
||||
mkdir(path.dirname(outputPath));
|
||||
|
||||
vulcanize.process(inputPath, (err, inlinedHtml) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
fs.writeFileSync(outputPath, inlinedHtml);
|
||||
});
|
||||
}
|
||||
|
||||
function vulcanizeRelease(releasePath) {
|
||||
const inputPath = path.join(rootPath, 'src', 'gui', 'html', 'index.html');
|
||||
const outputPath = path.join(releasePath.path, 'resources', 'ui', 'index.html');
|
||||
|
||||
vulcanizeFile(inputPath, outputPath);
|
||||
}
|
||||
|
||||
function vulcanizeTests(releasePath) {
|
||||
const inputPath = path.join(rootPath, 'src', 'tests', 'gui', 'html', 'elements');
|
||||
const outputPath = path.join(releasePath.path, 'html_tests', 'elements');
|
||||
|
||||
const tests = [
|
||||
'test_editable-table.html',
|
||||
'test_loot-custom-icons.html',
|
||||
'test_loot-dropdown-menu.html',
|
||||
'test_loot-menu.html',
|
||||
'test_loot-message-dialog.html',
|
||||
'test_loot-plugin-card.html',
|
||||
'test_loot-plugin-editor.html',
|
||||
'test_loot-plugin-item.html',
|
||||
'test_loot-search-toolbar.html',
|
||||
];
|
||||
|
||||
tests.forEach((test) => {
|
||||
vulcanizeFile(path.join(inputPath, test), path.join(outputPath, test));
|
||||
});
|
||||
}
|
||||
|
||||
// Run the appropriate build(s).
|
||||
const releasePaths = helpers.getAppReleasePaths(rootPath);
|
||||
|
||||
if (buildType === 'ui' || buildType === 'all') {
|
||||
releasePaths.forEach(vulcanizeRelease);
|
||||
}
|
||||
|
||||
if (buildType === 'tests' || buildType === 'all') {
|
||||
releasePaths.forEach(vulcanizeTests);
|
||||
}
|
||||
@@ -188,13 +188,19 @@
|
||||
</template>
|
||||
<script>
|
||||
'use strict';
|
||||
const editableTableHtml = document.currentScript.ownerDocument;
|
||||
function getRowTemplate(templateId) {
|
||||
return editableTableHtml.getElementById(templateId);
|
||||
}
|
||||
|
||||
|
||||
Polymer({ // eslint-disable-line new-cap, no-undef
|
||||
is: 'editable-table',
|
||||
extends: 'table',
|
||||
|
||||
attached() {
|
||||
/* Add "add new row" row. */
|
||||
const content = document.getElementById('newRow').content;
|
||||
const content = getRowTemplate('newRow').content;
|
||||
let row = document.importNode(content, true);
|
||||
this.tBodies[0].appendChild(row);
|
||||
row = this.tBodies[0].lastElementChild;
|
||||
@@ -323,7 +329,7 @@
|
||||
|
||||
addRow(tableData) {
|
||||
const rowTemplateId = this.getAttribute('data-template');
|
||||
const content = document.getElementById(rowTemplateId).content;
|
||||
const content = getRowTemplate(rowTemplateId).content;
|
||||
let row = document.importNode(content, true);
|
||||
this.tBodies[0].insertBefore(row, this.tBodies[0].lastElementChild);
|
||||
row = this.tBodies[0].lastElementChild.previousElementSibling;
|
||||
|
||||
@@ -47,11 +47,6 @@
|
||||
<link rel="import" href="../../../bower_components/paper-toggle-button/paper-toggle-button.html">
|
||||
<link rel="import" href="../../../bower_components/paper-toolbar/paper-toolbar.html">
|
||||
<link rel="import" href="../../../bower_components/paper-tooltip/paper-tooltip.html">
|
||||
</head>
|
||||
<!-- oncontextmenu attribute disables the right-click menu. -->
|
||||
<body oncontextmenu="return false" unresolved>
|
||||
<!-- This style element needs to be here so that it loads after the Vulcanized
|
||||
imports, which are placed in a div that is the first child of body. -->
|
||||
<style is="custom-style" include="paper-item-shared-styles">
|
||||
div[drawer],
|
||||
div[main] {
|
||||
@@ -147,6 +142,9 @@
|
||||
};
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<!-- oncontextmenu attribute disables the right-click menu. -->
|
||||
<body oncontextmenu="return false" unresolved>
|
||||
<paper-drawer-panel id="container" drawer-width="33%">
|
||||
<div drawer>
|
||||
<paper-toolbar>
|
||||
|
||||
@@ -10,9 +10,28 @@
|
||||
root.loot.translateStaticText = factory();
|
||||
}
|
||||
}(this, () => {
|
||||
function getTemplate(templateId, importHtml) {
|
||||
if (importHtml === undefined) {
|
||||
importHtml = templateId;
|
||||
}
|
||||
|
||||
let template = document.getElementById(templateId);
|
||||
if (!template) {
|
||||
template = document
|
||||
.querySelector(`link[rel="import"][href$="${importHtml}.html"]`).import;
|
||||
}
|
||||
template = template.querySelector(`#${templateId}`);
|
||||
|
||||
if (template.tagName !== 'TEMPLATE') {
|
||||
template = template.querySelector('template');
|
||||
}
|
||||
|
||||
return template.content;
|
||||
}
|
||||
|
||||
function translatePluginCardTemplate(l10n) {
|
||||
/* Plugin card template. */
|
||||
const pluginCard = document.getElementById('loot-plugin-card').querySelector('template').content;
|
||||
const pluginCard = getTemplate('loot-plugin-card');
|
||||
|
||||
pluginCard.querySelector('paper-tooltip[for=activeTick]').textContent = l10n.translate('Active Plugin');
|
||||
pluginCard.querySelector('paper-tooltip[for=isMaster]').textContent = l10n.translate('Master File');
|
||||
@@ -82,7 +101,7 @@
|
||||
|
||||
function translatePluginListItemTemplate(l10n) {
|
||||
/* Plugin List Item Template */
|
||||
const pluginItem = document.getElementById('loot-plugin-item').querySelector('template').content;
|
||||
const pluginItem = getTemplate('loot-plugin-item');
|
||||
|
||||
pluginItem.querySelector('#globalPriorityTooltip').textContent = l10n.translate('Global Priority');
|
||||
pluginItem.querySelector('#localPriorityTooltip').textContent = l10n.translate('Priority');
|
||||
@@ -92,7 +111,7 @@
|
||||
|
||||
function translateMessageDialogTemplate(l10n) {
|
||||
/* Plugin List Item Template */
|
||||
const messageDialog = document.getElementById('loot-message-dialog').querySelector('template').content;
|
||||
const messageDialog = getTemplate('loot-message-dialog');
|
||||
|
||||
messageDialog.getElementById('confirm').textContent = l10n.translate('OK');
|
||||
messageDialog.getElementById('dismiss').textContent = l10n.translate('Cancel');
|
||||
@@ -100,7 +119,7 @@
|
||||
|
||||
function translateFileRowTemplate(l10n) {
|
||||
/* File row template */
|
||||
const fileRow = document.getElementById('fileRow').content;
|
||||
const fileRow = getTemplate('fileRow', 'editable-table');
|
||||
|
||||
fileRow.querySelector('.name').setAttribute('error-message', l10n.translate('A filename is required.'));
|
||||
fileRow.querySelector('paper-tooltip').textContent = l10n.translate('Delete Row');
|
||||
@@ -108,7 +127,7 @@
|
||||
|
||||
function translateMessageRowTemplate(l10n) {
|
||||
/* Message row template */
|
||||
const messageRow = document.getElementById('messageRow').content;
|
||||
const messageRow = getTemplate('messageRow', 'editable-table');
|
||||
|
||||
messageRow.querySelector('.type').children[0].textContent = l10n.translate('Note');
|
||||
messageRow.querySelector('.type').children[1].textContent = l10n.translate('Warning');
|
||||
@@ -119,7 +138,7 @@
|
||||
|
||||
function translateTagRowTemplate(l10n) {
|
||||
/* Tag row template */
|
||||
const tagRow = document.getElementById('tagRow').content;
|
||||
const tagRow = getTemplate('tagRow', 'editable-table');
|
||||
|
||||
tagRow.querySelector('.type').children[0].textContent = l10n.translate('Add');
|
||||
tagRow.querySelector('.type').children[1].textContent = l10n.translate('Remove');
|
||||
@@ -129,7 +148,7 @@
|
||||
|
||||
function translateDirtyInfoRowTemplate(l10n) {
|
||||
/* Dirty Info row template */
|
||||
const dirtyInfoRow = document.getElementById('dirtyInfoRow').content;
|
||||
const dirtyInfoRow = getTemplate('dirtyInfoRow', 'editable-table');
|
||||
|
||||
dirtyInfoRow.querySelector('.crc').setAttribute('error-message', l10n.translate('A CRC is required.'));
|
||||
dirtyInfoRow.querySelector('.itm').setAttribute('error-message', l10n.translate('Values must be integers.'));
|
||||
@@ -141,7 +160,7 @@
|
||||
|
||||
function translateCleanInfoRowTemplate(l10n) {
|
||||
/* Dirty Info row template */
|
||||
const cleanInfoRow = document.getElementById('cleanInfoRow').content;
|
||||
const cleanInfoRow = getTemplate('cleanInfoRow', 'editable-table');
|
||||
|
||||
cleanInfoRow.querySelector('.crc').setAttribute('error-message', l10n.translate('A CRC is required.'));
|
||||
cleanInfoRow.querySelector('.utility').setAttribute('error-message', l10n.translate('A utility name is required.'));
|
||||
@@ -150,7 +169,7 @@
|
||||
|
||||
function translateLocationRowTemplate(l10n) {
|
||||
/* Location row template */
|
||||
const locationRow = document.getElementById('locationRow').content;
|
||||
const locationRow = getTemplate('locationRow', 'editable-table');
|
||||
|
||||
locationRow.querySelector('.link').setAttribute('error-message', l10n.translate('A link is required.'));
|
||||
locationRow.querySelector('paper-tooltip').textContent = l10n.translate('Delete Row');
|
||||
@@ -158,7 +177,7 @@
|
||||
|
||||
function translateGameRowTemplate(l10n) {
|
||||
/* Game row template */
|
||||
const gameRow = document.getElementById('gameRow').content;
|
||||
const gameRow = getTemplate('gameRow', 'editable-table');
|
||||
|
||||
gameRow.querySelector('.name').setAttribute('error-message', l10n.translate('A name is required.'));
|
||||
gameRow.querySelector('.folder').setAttribute('error-message', l10n.translate('A folder is required.'));
|
||||
@@ -167,7 +186,7 @@
|
||||
|
||||
function translateNewRowTemplate(l10n) {
|
||||
/* New row template */
|
||||
const newRow = document.getElementById('newRow').content;
|
||||
const newRow = getTemplate('newRow', 'editable-table');
|
||||
|
||||
newRow.querySelector('paper-tooltip').textContent = l10n.translate('Add New Row');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user