UNPKG

infuse.host

Version:

Infuse your HTML with dynamic content.

134 lines (110 loc) 5.58 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.uniqueIdFn = undefined; exports.default = createESModule; var _parseDocument = require('./parseDocument.js'); var _parseDocument2 = _interopRequireDefault(_parseDocument); var _parseTemplate = require('./parseTemplate.js'); var _parseTemplate2 = _interopRequireDefault(_parseTemplate); var _configs = require('./configs.js'); var _configs2 = _interopRequireDefault(_configs); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * Path to the configs module to use in generated ES modules. */ const DEFAULT_CONFIGS_PATH = 'infuse.host/src/configs.js'; /** * Generates a function that can be used to create unique IDs. The returned function is meant to be * used by the infuse.host parser to ensure that all parsed elements use unique IDs within a given * HTML document. The `hash` argument is used to make sure IDs generated for elements in one HTML * document don't conflict with those of another document. * * @function uniqueIdFn * @param {string} fullHash A hash to append to all IDs. * @param {number} [hashLength=7] Limits the length of the hash to the number of characters * specified by this attribute. Uses 7 by default, which means that the returned function will * only append the first 7 characters of the hash to the generated unique IDs. * @returns {Function} The return function takes a `tagName` (string) as the only argument and * returns a unique ID string. */ const uniqueIdFn = exports.uniqueIdFn = function createUniqueIdFunction(fullHash, hashLength = 7) { let idCounter = 1; const hash = hashLength > fullHash.length ? fullHash : fullHash.substr(0, hashLength); return tagName => `${tagName}${idCounter++}_${hash}`; }; /** * Generates an ES module from an HTML document. If the provided `htmlDocument` is a string, it is * parsed into an actual `Document`. It is then ran through the infuse.host parser and the source * code for an ES module is generated. The returned ES module exports: * * * The resulting `document`, after all the parsing is performed by infuse.host. * * Default export: The first template found in the exported `document`. * * All other templates found in `document`, using their predefined or generated * "template ids" as export names. * * The generated ES module also loads all the `contextFunctions`, generated by the infuse.host * parser, into memory. * * @function createESModule * @param {(string|Document)} htmlDocument The HTML document as a string (HTML source code) or as an * instance of `Document`. * @param {Object} [options={}] Options object. * @param {number} [options.hashLength=7] The length of the hash to use when generating unique * element IDs. The hash is used to avoid collisions with IDs of elements in other documents. * @param {Function} [options.uniqueId] A function to generate unique ID values during the * parsing process. * @param {string} [options.configsPath='infuse.host/src/configs'] Path to the infuse.host * configs module to use in the generated ES module. * @returns {string} The source code of the generated ES module. */ function createESModule(htmlDocument, options = {}) { const lines = ['']; const templateId = _configs2.default.get('templateId'); const configsPath = options.configsPath || DEFAULT_CONFIGS_PATH; // Parse the document. const { doctype, document, hash, window } = (0, _parseDocument2.default)(htmlDocument); // If a unique ID function was not provided, create one using the document's `hash`. const uniqueId = options.uniqueId || uniqueIdFn(hash, options.hashLength); // Find and parse all templates in the `document`. This changes the document's DOM. let templates = Array.from(document.querySelectorAll('template')); for (let i = 0; i < templates.length; i++) { (0, _parseTemplate2.default)(templates[i], { uniqueId, window }); } // Since the document's DOM changed, we need to find all templates again. templates = Array.from(document.querySelectorAll('template')); /** * Iterate over each template and add lines of code to `lines` to perform the following actions * when the generated ES module is loaded by the browser: * * * Export the template using its template ID. * * Add the template to the `parsedTemplates` map using its template ID. */ for (let i = 0; i < templates.length; i++) { const tid = templates[i].getAttribute(templateId); lines.splice(i, 0, `export const ${tid} = templates[${i}];`); lines.push(`parsedTemplates.set('${tid}', ${tid});`); } lines.push(''); /** * Iterate over the generated parsed elements `Map` and add code to `lines` so that its * contents are added to `contextFunctions` when the generated ES module is loaded by * the browser. */ for (const [tid, fn] of _configs.contextFunctions) { const fnSrc = fn.toString().replace('function anonymous(\n', 'function('); lines.push(`contextFunctions.set('${tid}', ${fnSrc});`); } // Clear context functions and templates from memory. _configs.contextFunctions.clear(); _configs.parsedTemplates.clear(); return `import { contextFunctions, parsedTemplates } from '${configsPath}'; const html = \`${doctype}\n${document.body.outerHTML.replace(/\\?`/g, '\\`')}\`; const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); const templates = doc.getElementsByTagName('template'); export { doc as document }; export default templates[0]; ${lines.join('\n')}`; }