Updated to nearly fully functional. Pre-release 1. Still needs some UI changes in the slideshow and admin pages (move the save button & fix the save detection for the internship list). Customer had one more page change request which I need to re-define and handle.

This commit is contained in:
Wynne Crisman
2018-12-12 11:04:00 -08:00
parent c0e971774e
commit 3fc374eda3
108 changed files with 3472 additions and 628 deletions

View File

@@ -0,0 +1,109 @@
// This plugin to Meteor uses the linter plugin api to package up all the templates indexed by name into a single file saved in the /private folder in the project.
// The file is intended to be used to service search engine requests for static html by utilizing server side rendering of the templates.
// Meteor packages up all the content not in /private or /public, so simply accessing the templates at runtime on the server does not appear to be an option.
const fs = Npm.require('fs');
const path = Npm.require('path');
//Disabled linter for now. Supposedly Google at least can manage SEO without server side rendering.
//Plugin.registerLinter({
// extensions: ["html"]
// //filenames: [".jshintrc"]
//}, () => new S3);
class S3 {
constructor() {
this.templateStart = /<template\s+name\s*=\s*(['"])\b(.*?)\1\s*>/;
this.templateEnd = /<\/\s*template\s*>\s*/;
}
extractTemplate(html, map) {
try {
let result = html.match(this.templateStart);
if(result != null) {
let name = result[2];
html = html.substring(result.index + result[0].length);
result = html.match(this.templateEnd);
//console.log("Matching: \n" + html);
//console.log("\n" + result);
if(result != null) {
let contents = html.substring(0, result.index);
html = html.substring(result.index + result[0].length);
//Save the template contents indexed by its name.
//TODO: Warn if we overwrite any template names??? Should never happen unless the developer made an error in naming.
map[name] = contents;
//console.log("Found template: " + name);
//console.log(contents);
}
else html = null;
}
else html = null;
} catch(err) {
console.log(err);
html = null;
}
return html;
}
processFilesForPackage(files, options) {
try {
let map = {};
//let fileNames = [];
let _this = this;
let originalContents = fs.readFileSync('private/template-index', {encoding: 'utf8', flag: 'r'});
let counter = 0;
//Extract all templates for every file processed (all HTML files).
files.forEach((file) => {
if(file._source.relPath.startsWith("imports/")) { /// && file._source.relPath === "imports/ui/Test.html"
let html = file.getContentsAsString();
while(html != null) {
//Will return null if no more templates are to be found.
html = this.extractTemplate(html, map);
}
}
});
//Returns a 16 character hex hash that is zero padded.
let hashString = (str)=> {
let hash = 0;
if(str.length === 0) return hash;
for(let i = 0; i < str.length; i++) {
let chr = str.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return ("0000000000000000" + hash.toString(16)).substr(-16);
};
let output = JSON.stringify(map);
//console.log("Out: " + output);
let hash = hashString(output);
//console.log("Hash: " + hash);
let originalHash = hashString(originalContents);
//console.log(originalHash);
if(originalHash !== hash) {
//console.log("Hash does not match!");
//Write the mapping to a 'template-index' file in the private folder.
fs.writeFile('private/template-index', output);
}
else {
//console.log("Hash matches..");
}
} catch(err) {
console.log(err);
}
}
}

View File

@@ -0,0 +1,17 @@
Package.describe({
name: "wcrisman:server-side-seo",
version: "1.0.0",
summary: "Copies all templates into an index file: /private/template-index for use at runtime to generate html for search engines to parse.",
documentation: "readme.md"
});
Package.registerBuildPlugin({
name: "wcrisman:server-side-seo-build",
sources: ["build.js"],
//npmDependencies: {'something-from-npm': '1.2.3'}
});
Package.onUse(function(api) {
//api.use('');
api.use('isobuild:linter-plugin@1.0.0');
});