@teleporthq/teleport-plugin-html-base-component
Advanced tools
Comparing version 0.34.0-alpha.0 to 0.34.0
@@ -11,13 +11,15 @@ import { | ||
const getMockComponentStructure = (): ComponentStructure => ({ | ||
chunks: [], | ||
options: { | ||
extractedResources: {}, | ||
}, | ||
uidl: component('Test', elementNode('container')), | ||
dependencies: {}, | ||
}) | ||
describe('plugin-html-base-component', () => { | ||
const { htmlComponentPlugin } = createHTMLBasePlugin() | ||
const structure: ComponentStructure = { | ||
chunks: [], | ||
options: {}, | ||
uidl: component('Test', elementNode('container')), | ||
dependencies: {}, | ||
} | ||
it('generated HAST nodes with the UIDL that is passed', async () => { | ||
const { chunks } = await htmlComponentPlugin(structure) | ||
const { htmlComponentPlugin } = createHTMLBasePlugin() | ||
const { chunks } = await htmlComponentPlugin(getMockComponentStructure()) | ||
const htmlChunk = chunks.find((chunk) => chunk.fileType === FileType.HTML) | ||
@@ -27,8 +29,9 @@ | ||
expect(htmlChunk).toBeDefined() | ||
expect(htmlChunk.name).toBe('html-template') | ||
expect(htmlChunk?.name).toBe('html-chunk') | ||
}) | ||
it('adds attributes to the HAST node', async () => { | ||
const { htmlComponentPlugin } = createHTMLBasePlugin() | ||
const { chunks } = await htmlComponentPlugin({ | ||
...structure, | ||
...getMockComponentStructure(), | ||
uidl: component( | ||
@@ -42,4 +45,4 @@ 'Test', | ||
expect(chunks.length).toEqual(2) | ||
expect(((chunks[1].content as HastNode).children[0] as HastNode).properties.href).toBe( | ||
expect(chunks.length).toEqual(1) | ||
expect(((chunks[0].content as HastNode).children[0] as HastNode).properties.href).toBe( | ||
'about.html' | ||
@@ -50,14 +53,16 @@ ) | ||
it('wraps static content inside div tags', async () => { | ||
const { htmlComponentPlugin } = createHTMLBasePlugin() | ||
const { chunks } = await htmlComponentPlugin({ | ||
...structure, | ||
...getMockComponentStructure(), | ||
uidl: component('Test', staticNode('Hello') as unknown as UIDLElementNode), | ||
}) | ||
expect(chunks.length).toEqual(3) | ||
expect((chunks[2].content as HastNode).children.length).toEqual(1) | ||
expect(chunks.length).toEqual(1) | ||
expect((chunks[0].content as HastNode).children.length).toEqual(1) | ||
}) | ||
it('Throws error when a external comp is missing', async () => { | ||
const { htmlComponentPlugin } = createHTMLBasePlugin() | ||
const plugin = htmlComponentPlugin({ | ||
...structure, | ||
...getMockComponentStructure(), | ||
uidl: component('Test', elementNode('Sample', {}, [], { type: 'local' })), | ||
@@ -70,4 +75,5 @@ }) | ||
it('Takes default value from props and state, when nodes are using dynamic ref', async () => { | ||
const { htmlComponentPlugin } = createHTMLBasePlugin() | ||
const { chunks } = await htmlComponentPlugin({ | ||
...structure, | ||
...getMockComponentStructure(), | ||
uidl: component('Test', elementNode('container', {}, [dynamicNode('prop', 'content')]), { | ||
@@ -79,6 +85,6 @@ content: { type: 'string', defaultValue: 'Hello World' }, | ||
const hastText = ( | ||
((chunks[3].content as HastNode).children[0] as HastNode).children[0] as HastNode | ||
((chunks[0].content as HastNode).children[0] as HastNode).children[0] as HastNode | ||
).children[0] as HastText | ||
expect(chunks.length).toEqual(4) | ||
expect(chunks.length).toEqual(1) | ||
expect(hastText).toBeDefined() | ||
@@ -85,0 +91,0 @@ expect(hastText.type).toBe('text') |
@@ -1,2 +0,2 @@ | ||
export declare const DEFAULT_COMPONENT_CHUNK_NAME = "html-template"; | ||
export declare const DEFAULT_COMPONENT_CHUNK_NAME = "html-chunk"; | ||
//# sourceMappingURL=constants.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.DEFAULT_COMPONENT_CHUNK_NAME = void 0; | ||
exports.DEFAULT_COMPONENT_CHUNK_NAME = 'html-template'; | ||
exports.DEFAULT_COMPONENT_CHUNK_NAME = 'html-chunk'; | ||
//# sourceMappingURL=constants.js.map |
@@ -8,3 +8,3 @@ import { ComponentPlugin, ComponentDefaultPluginParams, ComponentUIDL } from '@teleporthq/teleport-types'; | ||
htmlComponentPlugin: ComponentPlugin; | ||
addExternals: (list: Record<string, ComponentUIDL>) => void; | ||
addExternals: (list: Record<string, ComponentUIDL>, plugins: ComponentPlugin[]) => void; | ||
} | ||
@@ -11,0 +11,0 @@ type HtmlPluginFactory<T> = (config?: Partial<T & ComponentDefaultPluginParams>) => HtmlPlugin; |
@@ -59,17 +59,21 @@ "use strict"; | ||
var externals = {}; | ||
var addExternals = function (list) { | ||
var plugins = []; | ||
var addExternals = function (list, subComponentPlugins) { | ||
if (subComponentPlugins === void 0) { subComponentPlugins = []; } | ||
externals = __assign(__assign({}, externals), (list || {})); | ||
plugins = subComponentPlugins; | ||
}; | ||
var htmlComponentPlugin = function (structure) { return __awaiter(void 0, void 0, void 0, function () { | ||
var uidl, chunks, dependencies, options, _a, propDefinitions, _b, stateDefinitions, templatesLookUp, compBase, bodyContent; | ||
return __generator(this, function (_c) { | ||
switch (_c.label) { | ||
var uidl, _a, chunks, dependencies, options, _b, propDefinitions, _c, stateDefinitions, outputOptions, nodesLookup, compBase, subComponents, templateOptions, _i, _d, propKey, prop, bodyContent; | ||
return __generator(this, function (_e) { | ||
switch (_e.label) { | ||
case 0: | ||
uidl = structure.uidl, chunks = structure.chunks, dependencies = structure.dependencies, options = structure.options; | ||
_a = uidl.propDefinitions, propDefinitions = _a === void 0 ? {} : _a, _b = uidl.stateDefinitions, stateDefinitions = _b === void 0 ? {} : _b; | ||
templatesLookUp = {}; | ||
uidl = structure.uidl, _a = structure.chunks, chunks = _a === void 0 ? [] : _a, dependencies = structure.dependencies, options = structure.options; | ||
_b = uidl.propDefinitions, propDefinitions = _b === void 0 ? {} : _b, _c = uidl.stateDefinitions, stateDefinitions = _c === void 0 ? {} : _c, outputOptions = uidl.outputOptions; | ||
nodesLookup = {}; | ||
compBase = wrapComponent | ||
? teleport_plugin_common_1.HASTBuilders.createHTMLNode('body') | ||
: teleport_plugin_common_1.HASTBuilders.createHTMLNode('div'); | ||
return [4 /*yield*/, (0, node_handlers_1.generateHtmlSynatx)(uidl.node, templatesLookUp, propDefinitions, stateDefinitions, Object.values(externals).reduce(function (acc, comp) { | ||
subComponents = { | ||
externals: Object.values(externals).reduce(function (acc, comp) { | ||
teleport_shared_1.UIDLUtils.setFriendlyOutputOptions(comp); | ||
@@ -80,5 +84,25 @@ comp.name = teleport_shared_1.StringUtils.removeIllegalCharacters(comp.name) || 'AppComponent'; | ||
return acc; | ||
}, {}), options.projectRouteDefinition, { chunks: chunks, dependencies: dependencies, options: options })]; | ||
}, {}), | ||
plugins: plugins, | ||
}; | ||
templateOptions = { chunks: chunks, dependencies: dependencies, options: options, outputOptions: outputOptions }; | ||
_i = 0, _d = Object.keys(propDefinitions); | ||
_e.label = 1; | ||
case 1: | ||
bodyContent = _c.sent(); | ||
if (!(_i < _d.length)) return [3 /*break*/, 4]; | ||
propKey = _d[_i]; | ||
prop = propDefinitions[propKey]; | ||
if (!(prop.type === 'element' && | ||
prop.defaultValue !== undefined && | ||
typeof prop.defaultValue === 'object')) return [3 /*break*/, 3]; | ||
return [4 /*yield*/, (0, node_handlers_1.generateHtmlSyntax)(prop.defaultValue, nodesLookup, propDefinitions, stateDefinitions, subComponents, templateOptions)]; | ||
case 2: | ||
_e.sent(); | ||
_e.label = 3; | ||
case 3: | ||
_i++; | ||
return [3 /*break*/, 1]; | ||
case 4: return [4 /*yield*/, (0, node_handlers_1.generateHtmlSyntax)(uidl.node, nodesLookup, propDefinitions, stateDefinitions, subComponents, templateOptions)]; | ||
case 5: | ||
bodyContent = _e.sent(); | ||
teleport_plugin_common_1.HASTUtils.addChildNode(compBase, bodyContent); | ||
@@ -92,3 +116,3 @@ chunks.push({ | ||
meta: { | ||
nodesLookup: templatesLookUp, | ||
nodesLookup: nodesLookup, | ||
}, | ||
@@ -95,0 +119,0 @@ }); |
@@ -1,9 +0,13 @@ | ||
import { UIDLNode, HastNode, UIDLPropDefinition, UIDLStateDefinition, HastText, ComponentUIDL, ChunkDefinition, UIDLDependency, GeneratorOptions, UIDLRouteDefinitions } from '@teleporthq/teleport-types'; | ||
type NodeToHTML<NodeType, ReturnType> = (node: NodeType, templatesLookUp: Record<string, unknown>, propDefinitions: Record<string, UIDLPropDefinition>, stateDefinitions: Record<string, UIDLStateDefinition>, externals: Record<string, ComponentUIDL>, routeDefinitions: UIDLRouteDefinitions, structure: { | ||
import { UIDLNode, HastNode, UIDLPropDefinition, UIDLStateDefinition, HastText, ComponentUIDL, ChunkDefinition, UIDLDependency, GeneratorOptions, ComponentPlugin, UIDLComponentOutputOptions } from '@teleporthq/teleport-types'; | ||
type NodeToHTML<NodeType, ReturnType> = (node: NodeType, nodesLookup: Record<string, HastNode | HastText>, propDefinitions: Record<string, UIDLPropDefinition>, stateDefinitions: Record<string, UIDLStateDefinition>, subComponentOptions: { | ||
externals: Record<string, ComponentUIDL>; | ||
plugins: ComponentPlugin[]; | ||
}, structure: { | ||
chunks: ChunkDefinition[]; | ||
dependencies: Record<string, UIDLDependency>; | ||
options: GeneratorOptions; | ||
outputOptions: UIDLComponentOutputOptions; | ||
}) => ReturnType; | ||
export declare const generateHtmlSynatx: NodeToHTML<UIDLNode, Promise<HastNode | HastText>>; | ||
export declare const generateHtmlSyntax: NodeToHTML<UIDLNode, Promise<HastNode | HastText>>; | ||
export {}; | ||
//# sourceMappingURL=node-handlers.d.ts.map |
@@ -59,4 +59,5 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.generateHtmlSynatx = void 0; | ||
exports.generateHtmlSyntax = void 0; | ||
var teleport_types_1 = require("@teleporthq/teleport-types"); | ||
var path_1 = require("path"); | ||
var teleport_plugin_common_1 = require("@teleporthq/teleport-plugin-common"); | ||
@@ -67,31 +68,49 @@ var teleport_shared_1 = require("@teleporthq/teleport-shared"); | ||
var constants_1 = require("./constants"); | ||
var generateHtmlSynatx = function (node, templatesLookUp, propDefinitions, stateDefinitions, externals, routeDefinitions, structure) { return __awaiter(void 0, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
switch (node.type) { | ||
case 'inject': | ||
case 'raw': | ||
return [2 /*return*/, teleport_plugin_common_1.HASTBuilders.createTextNode(node.content.toString())]; | ||
case 'static': | ||
return [2 /*return*/, teleport_plugin_common_1.HASTBuilders.createTextNode(teleport_shared_1.StringUtils.encode(node.content.toString()))]; | ||
case 'slot': | ||
return [2 /*return*/, teleport_plugin_common_1.HASTBuilders.createHTMLNode(node.type)]; | ||
case 'element': | ||
return [2 /*return*/, generatElementNode(node, templatesLookUp, propDefinitions, stateDefinitions, externals, routeDefinitions, structure)]; | ||
case 'dynamic': | ||
return [2 /*return*/, generateDynamicNode(node, templatesLookUp, propDefinitions, stateDefinitions, externals, routeDefinitions, structure)]; | ||
default: | ||
throw new teleport_types_1.HTMLComponentGeneratorError("generateHtmlSyntax encountered a node of unsupported type: ".concat(JSON.stringify(node, null, 2), " ")); | ||
var isValidURL = function (url) { | ||
try { | ||
/* tslint:disable:no-unused-expression */ | ||
new URL(url); | ||
return true; | ||
} | ||
catch (error) { | ||
return false; | ||
} | ||
}; | ||
var generateHtmlSyntax = function (node, nodesLookup, propDefinitions, stateDefinitions, subComponentOptions, structure) { return __awaiter(void 0, void 0, void 0, function () { | ||
var _a, elementNode, dynamicNode; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
_a = node.type; | ||
switch (_a) { | ||
case 'inject': return [3 /*break*/, 1]; | ||
case 'raw': return [3 /*break*/, 1]; | ||
case 'static': return [3 /*break*/, 2]; | ||
case 'slot': return [3 /*break*/, 3]; | ||
case 'element': return [3 /*break*/, 4]; | ||
case 'dynamic': return [3 /*break*/, 6]; | ||
} | ||
return [3 /*break*/, 8]; | ||
case 1: return [2 /*return*/, teleport_plugin_common_1.HASTBuilders.createTextNode(node.content.toString())]; | ||
case 2: return [2 /*return*/, teleport_plugin_common_1.HASTBuilders.createTextNode(teleport_shared_1.StringUtils.encode(node.content.toString()))]; | ||
case 3: return [2 /*return*/, teleport_plugin_common_1.HASTBuilders.createHTMLNode(node.type)]; | ||
case 4: return [4 /*yield*/, generateElementNode(node, nodesLookup, propDefinitions, stateDefinitions, subComponentOptions, structure)]; | ||
case 5: | ||
elementNode = _b.sent(); | ||
return [2 /*return*/, elementNode]; | ||
case 6: return [4 /*yield*/, generateDynamicNode(node, nodesLookup, propDefinitions, stateDefinitions, subComponentOptions, structure)]; | ||
case 7: | ||
dynamicNode = _b.sent(); | ||
return [2 /*return*/, dynamicNode]; | ||
case 8: throw new teleport_types_1.HTMLComponentGeneratorError("generateHtmlSyntax encountered a node of unsupported type: ".concat(JSON.stringify(node, null, 2), " ")); | ||
} | ||
return [2 /*return*/]; | ||
}); | ||
}); }; | ||
exports.generateHtmlSynatx = generateHtmlSynatx; | ||
var generatElementNode = function (node, templatesLookUp, propDefinitions, stateDefinitions, externals, routeDefinitions, structure) { return __awaiter(void 0, void 0, void 0, function () { | ||
var _a, elementType, children, _b, attrs, _c, style, _d, referencedStyles, dependency, key, elementNode, dependencies, compTag, _i, children_1, child, childTag; | ||
return __generator(this, function (_e) { | ||
switch (_e.label) { | ||
exports.generateHtmlSyntax = generateHtmlSyntax; | ||
var generateElementNode = function (node, nodesLookup, propDefinitions, stateDefinitions, subComponentOptions, structure) { return __awaiter(void 0, void 0, void 0, function () { | ||
var _a, elementType, children, _b, attrs, _c, style, _d, referencedStyles, dependency, key, dependencies, _i, _e, attrKey, attr, compTag, elementNode, _f, children_1, child, childTag; | ||
return __generator(this, function (_g) { | ||
switch (_g.label) { | ||
case 0: | ||
_a = node.content, elementType = _a.elementType, children = _a.children, _b = _a.attrs, attrs = _b === void 0 ? {} : _b, _c = _a.style, style = _c === void 0 ? {} : _c, _d = _a.referencedStyles, referencedStyles = _d === void 0 ? {} : _d, dependency = _a.dependency, key = _a.key; | ||
elementNode = teleport_plugin_common_1.HASTBuilders.createHTMLNode(elementType); | ||
templatesLookUp[key] = elementNode; | ||
dependencies = structure.dependencies; | ||
@@ -101,20 +120,32 @@ if (dependency && (dependency === null || dependency === void 0 ? void 0 : dependency.type) !== 'local') { | ||
} | ||
if (!(dependency && (dependency === null || dependency === void 0 ? void 0 : dependency.type) === 'local')) return [3 /*break*/, 2]; | ||
return [4 /*yield*/, generateComponentContent(node, propDefinitions, stateDefinitions, externals, routeDefinitions, structure)]; | ||
if (!(dependency && (dependency === null || dependency === void 0 ? void 0 : dependency.type) === 'local')) return [3 /*break*/, 6]; | ||
_i = 0, _e = Object.keys(attrs); | ||
_g.label = 1; | ||
case 1: | ||
compTag = _e.sent(); | ||
return [2 /*return*/, compTag]; | ||
if (!(_i < _e.length)) return [3 /*break*/, 4]; | ||
attrKey = _e[_i]; | ||
attr = attrs[attrKey]; | ||
if (!(attr.type === 'element')) return [3 /*break*/, 3]; | ||
return [4 /*yield*/, generateElementNode(attr, nodesLookup, propDefinitions, stateDefinitions, subComponentOptions, structure)]; | ||
case 2: | ||
if (!children) return [3 /*break*/, 6]; | ||
_i = 0, children_1 = children; | ||
_e.label = 3; | ||
_g.sent(); | ||
_g.label = 3; | ||
case 3: | ||
if (!(_i < children_1.length)) return [3 /*break*/, 6]; | ||
child = children_1[_i]; | ||
return [4 /*yield*/, (0, exports.generateHtmlSynatx)(child, templatesLookUp, propDefinitions, stateDefinitions, externals, routeDefinitions, structure)]; | ||
case 4: | ||
childTag = _e.sent(); | ||
if (!childTag) { | ||
return [2 /*return*/]; | ||
} | ||
_i++; | ||
return [3 /*break*/, 1]; | ||
case 4: return [4 /*yield*/, generateComponentContent(node, nodesLookup, propDefinitions, stateDefinitions, subComponentOptions, structure)]; | ||
case 5: | ||
compTag = _g.sent(); | ||
return [2 /*return*/, compTag]; | ||
case 6: | ||
elementNode = teleport_plugin_common_1.HASTBuilders.createHTMLNode(elementType); | ||
if (!children) return [3 /*break*/, 10]; | ||
_f = 0, children_1 = children; | ||
_g.label = 7; | ||
case 7: | ||
if (!(_f < children_1.length)) return [3 /*break*/, 10]; | ||
child = children_1[_f]; | ||
return [4 /*yield*/, (0, exports.generateHtmlSyntax)(child, nodesLookup, propDefinitions, stateDefinitions, subComponentOptions, structure)]; | ||
case 8: | ||
childTag = _g.sent(); | ||
if (typeof childTag === 'string') { | ||
@@ -126,7 +157,7 @@ teleport_plugin_common_1.HASTUtils.addTextNode(elementNode, childTag); | ||
} | ||
_e.label = 5; | ||
case 5: | ||
_i++; | ||
return [3 /*break*/, 3]; | ||
case 6: | ||
_g.label = 9; | ||
case 9: | ||
_f++; | ||
return [3 /*break*/, 7]; | ||
case 10: | ||
if (Object.keys(referencedStyles).length > 0) { | ||
@@ -144,5 +175,4 @@ Object.keys(referencedStyles).forEach(function (styleRef) { | ||
} | ||
if (Object.keys(attrs).length > 0) { | ||
handleAttributes(elementNode, attrs, propDefinitions, stateDefinitions, routeDefinitions); | ||
} | ||
handleAttributes(elementType, elementNode, attrs, propDefinitions, stateDefinitions, structure.options.projectRouteDefinition, structure.outputOptions); | ||
nodesLookup[key] = elementNode; | ||
return [2 /*return*/, elementNode]; | ||
@@ -152,19 +182,21 @@ } | ||
}); }; | ||
var generateComponentContent = function (node, propDefinitions, stateDefinitions, externals, routeDefinitions, structure) { return __awaiter(void 0, void 0, void 0, function () { | ||
var _a, elementType, _b, attrs, key, _c, children, dependencies, chunks, options, comp, lookUpTemplates, compHasSlots, combinedProps, propsForInstance, combinedStates, statesForInstance, elementNode, compTag, cssPlugin, result, chunk, styleChunk; | ||
var _d; | ||
return __generator(this, function (_e) { | ||
switch (_e.label) { | ||
var generateComponentContent = function (node, nodesLookup, propDefinitions, stateDefinitions, subComponentOptions, structure) { return __awaiter(void 0, void 0, void 0, function () { | ||
var externals, plugins, _a, elementType, _b, attrs, key, _c, children, dependencies, _d, chunks, options, compName, component, componentClone, compHasSlots, combinedProps, propsForInstance, _i, _e, propKey, combinedStates, statesForInstance, componentWrapper, isExistingNode, componentInstanceToGenerate, compTag, cssPlugin, initialStructure, result, chunk, styleChunk; | ||
var _f, _g, _h; | ||
return __generator(this, function (_j) { | ||
switch (_j.label) { | ||
case 0: | ||
externals = subComponentOptions.externals, plugins = subComponentOptions.plugins; | ||
_a = node.content, elementType = _a.elementType, _b = _a.attrs, attrs = _b === void 0 ? {} : _b, key = _a.key, _c = _a.children, children = _c === void 0 ? [] : _c; | ||
dependencies = structure.dependencies, chunks = structure.chunks, options = structure.options; | ||
comp = teleport_shared_1.UIDLUtils.cloneObject(externals[elementType] || {}); | ||
lookUpTemplates = {}; | ||
dependencies = structure.dependencies, _d = structure.chunks, chunks = _d === void 0 ? [] : _d, options = structure.options; | ||
compName = elementType === 'Component' ? 'AppComponent' : elementType; | ||
component = externals[compName]; | ||
if (component === undefined) { | ||
throw new teleport_types_1.HTMLComponentGeneratorError("".concat(compName, " is missing from externals object")); | ||
} | ||
componentClone = teleport_shared_1.UIDLUtils.cloneObject(component); | ||
compHasSlots = false; | ||
if (!comp || !(comp === null || comp === void 0 ? void 0 : comp.node)) { | ||
throw new teleport_types_1.HTMLComponentGeneratorError("".concat(elementType, " is not found from the externals. \n\n Received ").concat(JSON.stringify(Object.keys(externals), null, 2))); | ||
} | ||
if (children.length) { | ||
compHasSlots = true; | ||
teleport_shared_1.UIDLUtils.traverseNodes(comp.node, function (childNode, parentNode) { | ||
teleport_shared_1.UIDLUtils.traverseNodes(componentClone.node, function (childNode, parentNode) { | ||
var _a, _b; | ||
@@ -197,14 +229,18 @@ if (childNode.type === 'slot' && parentNode.type === 'element') { | ||
} | ||
combinedProps = __assign(__assign({}, propDefinitions), ((comp === null || comp === void 0 ? void 0 : comp.propDefinitions) || {})); | ||
propsForInstance = Object.keys(combinedProps).reduce(function (acc, propKey) { | ||
var _a, _b; | ||
if (attrs[propKey]) { | ||
acc[propKey] = __assign(__assign({}, combinedProps[propKey]), { defaultValue: ((_a = attrs[propKey]) === null || _a === void 0 ? void 0 : _a.content) || ((_b = combinedProps[propKey]) === null || _b === void 0 ? void 0 : _b.defaultValue) }); | ||
combinedProps = __assign(__assign({}, propDefinitions), ((componentClone === null || componentClone === void 0 ? void 0 : componentClone.propDefinitions) || {})); | ||
propsForInstance = {}; | ||
for (_i = 0, _e = Object.keys(combinedProps); _i < _e.length; _i++) { | ||
propKey = _e[_i]; | ||
// If the attribute is a named-slot, then we can directly pass the value instead of just the content | ||
if (((_f = attrs[propKey]) === null || _f === void 0 ? void 0 : _f.type) === 'element') { | ||
propsForInstance[propKey] = __assign(__assign({}, combinedProps[propKey]), { defaultValue: attrs[propKey] }); | ||
} | ||
else if (attrs[propKey]) { | ||
propsForInstance[propKey] = __assign(__assign({}, combinedProps[propKey]), { defaultValue: ((_g = attrs[propKey]) === null || _g === void 0 ? void 0 : _g.content) || ((_h = combinedProps[propKey]) === null || _h === void 0 ? void 0 : _h.defaultValue) }); | ||
} | ||
else { | ||
acc[propKey] = combinedProps[propKey]; | ||
propsForInstance[propKey] = combinedProps[propKey]; | ||
} | ||
return acc; | ||
}, {}); | ||
combinedStates = __assign(__assign({}, stateDefinitions), ((comp === null || comp === void 0 ? void 0 : comp.stateDefinitions) || {})); | ||
} | ||
combinedStates = __assign(__assign({}, stateDefinitions), ((componentClone === null || componentClone === void 0 ? void 0 : componentClone.stateDefinitions) || {})); | ||
statesForInstance = Object.keys(combinedStates).reduce(function (acc, propKey) { | ||
@@ -220,37 +256,62 @@ var _a, _b; | ||
}, {}); | ||
elementNode = teleport_plugin_common_1.HASTBuilders.createHTMLNode(teleport_shared_1.StringUtils.camelCaseToDashCase(elementType)); | ||
lookUpTemplates[key] = elementNode; | ||
return [4 /*yield*/, (0, exports.generateHtmlSynatx)(__assign(__assign({}, comp.node), { content: __assign(__assign({}, comp.node.content), { style: __assign(__assign({}, (((_d = comp.node.content) === null || _d === void 0 ? void 0 : _d.style) || {})), { display: { | ||
type: 'static', | ||
content: 'contents', | ||
} }) }) }), lookUpTemplates, propsForInstance, statesForInstance, externals, routeDefinitions, structure)]; | ||
componentWrapper = teleport_shared_1.StringUtils.camelCaseToDashCase("".concat(compName, "-wrapper")); | ||
isExistingNode = nodesLookup[componentWrapper]; | ||
if (isExistingNode !== undefined) { | ||
componentWrapper = "".concat(componentWrapper, "-").concat(teleport_shared_1.StringUtils.generateRandomString()); | ||
} | ||
componentInstanceToGenerate = { | ||
type: 'element', | ||
content: { | ||
elementType: componentWrapper, | ||
key: componentWrapper, | ||
children: [componentClone.node], | ||
style: { | ||
display: { | ||
type: 'static', | ||
content: 'contents', | ||
}, | ||
}, | ||
}, | ||
}; | ||
return [4 /*yield*/, (0, exports.generateHtmlSyntax)(componentInstanceToGenerate, nodesLookup, propsForInstance, statesForInstance, subComponentOptions, structure)]; | ||
case 1: | ||
compTag = (_e.sent()); | ||
compTag = _j.sent(); | ||
cssPlugin = (0, teleport_plugin_css_1.createCSSPlugin)({ | ||
templateStyle: 'html', | ||
templateChunkName: 'html-template', | ||
templateChunkName: constants_1.DEFAULT_COMPONENT_CHUNK_NAME, | ||
declareDependency: 'import', | ||
forceScoping: true, | ||
chunkName: comp.name, | ||
chunkName: componentClone.name, | ||
staticPropReferences: true, | ||
}); | ||
return [4 /*yield*/, cssPlugin({ | ||
uidl: __assign(__assign({}, comp), { propDefinitions: propsForInstance, stateDefinitions: statesForInstance }), | ||
chunks: [ | ||
{ | ||
type: teleport_types_1.ChunkType.HAST, | ||
fileType: teleport_types_1.FileType.HTML, | ||
name: constants_1.DEFAULT_COMPONENT_CHUNK_NAME, | ||
linkAfter: [], | ||
content: compTag, | ||
meta: { | ||
nodesLookup: lookUpTemplates, | ||
}, | ||
initialStructure = { | ||
uidl: __assign(__assign({}, componentClone), { propDefinitions: propsForInstance, stateDefinitions: statesForInstance }), | ||
chunks: [ | ||
{ | ||
type: teleport_types_1.ChunkType.HAST, | ||
fileType: teleport_types_1.FileType.HTML, | ||
name: constants_1.DEFAULT_COMPONENT_CHUNK_NAME, | ||
linkAfter: [], | ||
content: compTag, | ||
meta: { | ||
nodesLookup: nodesLookup, | ||
}, | ||
], | ||
dependencies: dependencies, | ||
options: options, | ||
})]; | ||
}, | ||
], | ||
dependencies: dependencies, | ||
options: options, | ||
}; | ||
return [4 /*yield*/, __spreadArray([cssPlugin], plugins, true).reduce(function (previousPluginOperation, plugin) { return __awaiter(void 0, void 0, void 0, function () { | ||
var modifiedStructure; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, previousPluginOperation]; | ||
case 1: | ||
modifiedStructure = _a.sent(); | ||
return [2 /*return*/, plugin(modifiedStructure)]; | ||
} | ||
}); | ||
}); }, Promise.resolve(initialStructure))]; | ||
case 2: | ||
result = _e.sent(); | ||
result = _j.sent(); | ||
if (compHasSlots) { | ||
@@ -264,8 +325,12 @@ result.chunks.forEach(function (chunk) { | ||
else { | ||
chunk = chunks.find(function (item) { return item.name === comp.name; }); | ||
chunk = chunks.find(function (item) { return item.name === componentClone.name; }); | ||
if (!chunk) { | ||
styleChunk = result.chunks.find(function (item) { return item.name === comp.name; }); | ||
styleChunk = result.chunks.find(function (item) { return item.fileType === teleport_types_1.FileType.CSS; }); | ||
if (!styleChunk) { | ||
return [2 /*return*/]; | ||
} | ||
chunks.push(styleChunk); | ||
} | ||
} | ||
nodesLookup[key] = compTag; | ||
return [2 /*return*/, compTag]; | ||
@@ -275,10 +340,26 @@ } | ||
}); }; | ||
var generateDynamicNode = function (node, _, propDefinitions, stateDefinitions) { | ||
var spanTag = teleport_plugin_common_1.HASTBuilders.createHTMLNode('span'); | ||
var usedReferenceValue = node.content.referenceType === 'prop' | ||
? getValueFromReference(node.content.id, propDefinitions) | ||
: getValueFromReference(node.content.id, stateDefinitions); | ||
teleport_plugin_common_1.HASTUtils.addTextNode(spanTag, String(usedReferenceValue)); | ||
return spanTag; | ||
}; | ||
var generateDynamicNode = function (node, nodesLookup, propDefinitions, stateDefinitions, subComponentOptions, structure) { return __awaiter(void 0, void 0, void 0, function () { | ||
var usedReferenceValue, slotNode, spanTagWrapper, commentNode, spanTag; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
usedReferenceValue = getValueFromReference(node.content.id, node.content.referenceType === 'prop' ? propDefinitions : stateDefinitions); | ||
if (!(usedReferenceValue.type === 'element' && usedReferenceValue.defaultValue)) return [3 /*break*/, 2]; | ||
return [4 /*yield*/, generateElementNode(usedReferenceValue.defaultValue, nodesLookup, propDefinitions, stateDefinitions, subComponentOptions, structure)]; | ||
case 1: | ||
slotNode = _a.sent(); | ||
return [2 /*return*/, slotNode]; | ||
case 2: | ||
if (usedReferenceValue.type === 'element' && usedReferenceValue.defaultValue === undefined) { | ||
spanTagWrapper = teleport_plugin_common_1.HASTBuilders.createHTMLNode('span'); | ||
commentNode = teleport_plugin_common_1.HASTBuilders.createComment("Content for slot ".concat(node.content.id)); | ||
teleport_plugin_common_1.HASTUtils.addChildNode(spanTagWrapper, commentNode); | ||
return [2 /*return*/, spanTagWrapper]; | ||
} | ||
spanTag = teleport_plugin_common_1.HASTBuilders.createHTMLNode('span'); | ||
teleport_plugin_common_1.HASTUtils.addTextNode(spanTag, String(usedReferenceValue.defaultValue)); | ||
return [2 /*return*/, spanTag]; | ||
} | ||
}); | ||
}); }; | ||
var handleStyles = function (node, styles, propDefinitions, stateDefinitions) { | ||
@@ -289,8 +370,6 @@ Object.keys(styles).forEach(function (styleKey) { | ||
if (style.type === 'dynamic' && ((_a = style.content) === null || _a === void 0 ? void 0 : _a.referenceType) !== 'token') { | ||
if (style.content.referenceType === 'prop') { | ||
style = getValueFromReference(style.content.id, propDefinitions); | ||
var referencedValue = getValueFromReference(style.content.id, style.content.referenceType === 'prop' ? propDefinitions : stateDefinitions); | ||
if (referencedValue.type === 'string' || referencedValue.type === 'number') { | ||
style = String(referencedValue.defaultValue); | ||
} | ||
else if (style.content.referenceType === 'state') { | ||
style = getValueFromReference(style.content.id, stateDefinitions); | ||
} | ||
node.content.style[styleKey] = typeof style === 'string' ? (0, teleport_uidl_builders_1.staticNode)(style) : style; | ||
@@ -300,38 +379,81 @@ } | ||
}; | ||
var handleAttributes = function (htmlNode, attrs, propDefinitions, stateDefinitions, routeDefinitions) { | ||
Object.keys(attrs).forEach(function (attrKey) { | ||
var handleAttributes = function (elementType, htmlNode, attrs, propDefinitions, stateDefinitions, routeDefinitions, outputOptions) { | ||
var _loop_1 = function (attrKey) { | ||
var attrValue = attrs[attrKey]; | ||
if (attrKey === 'href' && | ||
attrValue.type === 'static' && | ||
typeof attrValue.content === 'string' && | ||
attrValue.content.startsWith('/')) { | ||
attrValue = | ||
attrValue.content === '/' || | ||
attrValue.content === | ||
"/".concat(teleport_shared_1.StringUtils.camelCaseToDashCase(teleport_shared_1.StringUtils.removeIllegalCharacters((routeDefinitions === null || routeDefinitions === void 0 ? void 0 : routeDefinitions.defaultValue) || ''))) | ||
? (0, teleport_uidl_builders_1.staticNode)('index.html') | ||
: (0, teleport_uidl_builders_1.staticNode)("".concat(attrValue.content.split('/').pop(), ".html")); | ||
teleport_plugin_common_1.HASTUtils.addAttributeToNode(htmlNode, attrKey, String(attrValue.content)); | ||
return; | ||
var type = attrValue.type, content = attrValue.content; | ||
switch (type) { | ||
case 'static': { | ||
if (attrKey === 'href' && typeof content === 'string' && content.startsWith('/')) { | ||
var targetLink = void 0; | ||
var targetRoute = ((routeDefinitions === null || routeDefinitions === void 0 ? void 0 : routeDefinitions.values) || []).find(function (route) { return route.pageOptions.navLink === content; }); | ||
if (targetRoute) { | ||
targetLink = targetRoute.pageOptions.navLink; | ||
} | ||
if (!targetRoute && content === '/home') { | ||
targetLink = '/'; | ||
} | ||
if (!targetLink && !targetRoute) { | ||
targetLink = content; | ||
} | ||
var currentPageRoute = path_1.join.apply(void 0, __spreadArray(__spreadArray([], ((outputOptions === null || outputOptions === void 0 ? void 0 : outputOptions.folderPath) || []), false), ['./'], false)); | ||
var localPrefix = (0, path_1.relative)("/".concat(currentPageRoute), "/".concat(targetLink === '/' ? 'index' : targetLink)); | ||
teleport_plugin_common_1.HASTUtils.addAttributeToNode(htmlNode, attrKey, "".concat(localPrefix, ".html")); | ||
break; | ||
} | ||
if (typeof content === 'boolean') { | ||
htmlNode.properties[attrKey] = content === true ? 'true' : 'false'; | ||
} | ||
else if (typeof content === 'string' || typeof attrValue.content === 'number') { | ||
var value = teleport_shared_1.StringUtils.encode(String(attrValue.content)); | ||
/* | ||
elementType of image is always mapped to img. | ||
For reference, check `html-mapping` file. | ||
*/ | ||
if (elementType === 'img' && attrKey === 'src' && !isValidURL(value)) { | ||
/* | ||
By default we just prefix all the asset paths with just the | ||
assetPrefix that is configured in the project. But for `html` generators | ||
we need to prefix that with the current file location. | ||
Because, all the other frameworks have a build setup. which serves all the | ||
assets from the `public` folder. But in the case of `html` here is how it works | ||
We load a file from `index.html` the request for the image goes from | ||
'...url.../public/...image...' | ||
If it's a nested url, then the request goes from | ||
'...url/nested/public/...image..' | ||
But the nested folder is available only on the root. With this | ||
The url changes prefixes to | ||
../public/playground_assets/..image.. etc depending on the dept the file is in. | ||
*/ | ||
value = (0, path_1.join)((0, path_1.relative)(path_1.join.apply(void 0, outputOptions.folderPath), './'), value); | ||
} | ||
teleport_plugin_common_1.HASTUtils.addAttributeToNode(htmlNode, attrKey, value); | ||
} | ||
break; | ||
} | ||
case 'dynamic': { | ||
var value = getValueFromReference(content.id, content.referenceType === 'prop' ? propDefinitions : stateDefinitions); | ||
teleport_plugin_common_1.HASTUtils.addAttributeToNode(htmlNode, attrKey, String(value.defaultValue)); | ||
break; | ||
} | ||
case 'raw': { | ||
teleport_plugin_common_1.HASTUtils.addAttributeToNode(htmlNode, attrKey, content); | ||
break; | ||
} | ||
case 'element': | ||
case 'import': | ||
case 'expr': | ||
break; | ||
default: { | ||
throw new teleport_types_1.HTMLComponentGeneratorError("Received ".concat(JSON.stringify(attrValue, null, 2), " \n in handleAttributes for html")); | ||
} | ||
} | ||
if (attrValue.type === 'dynamic') { | ||
var value = attrValue.content.referenceType === 'prop' | ||
? getValueFromReference(attrValue.content.id, propDefinitions) | ||
: getValueFromReference(attrValue.content.id, stateDefinitions); | ||
teleport_plugin_common_1.HASTUtils.addAttributeToNode(htmlNode, attrKey, String(value)); | ||
return; | ||
} | ||
if (attrValue.type === 'raw') { | ||
teleport_plugin_common_1.HASTUtils.addAttributeToNode(htmlNode, attrKey, attrValue.content); | ||
return; | ||
} | ||
if (typeof attrValue.content === 'boolean') { | ||
teleport_plugin_common_1.HASTUtils.addBooleanAttributeToNode(htmlNode, attrKey); | ||
return; | ||
} | ||
else if (typeof attrValue.content === 'string' || typeof attrValue.content === 'number') { | ||
teleport_plugin_common_1.HASTUtils.addAttributeToNode(htmlNode, attrKey, teleport_shared_1.StringUtils.encode(String(attrValue.content))); | ||
return; | ||
} | ||
}); | ||
}; | ||
for (var _i = 0, _a = Object.keys(attrs); _i < _a.length; _i++) { | ||
var attrKey = _a[_i]; | ||
_loop_1(attrKey); | ||
} | ||
}; | ||
@@ -343,10 +465,11 @@ var getValueFromReference = function (key, definitions) { | ||
} | ||
if (!usedReferenceValue.hasOwnProperty('defaultValue')) { | ||
if (['string', 'number', 'object', 'element'].includes(usedReferenceValue === null || usedReferenceValue === void 0 ? void 0 : usedReferenceValue.type) === false) { | ||
throw new teleport_types_1.HTMLComponentGeneratorError("Attribute is using dynamic value, but received of type ".concat(JSON.stringify(usedReferenceValue, null, 2))); | ||
} | ||
if (usedReferenceValue.type !== 'element' && | ||
usedReferenceValue.hasOwnProperty('defaultValue') === false) { | ||
throw new teleport_types_1.HTMLComponentGeneratorError("Default value is missing from dynamic reference - ".concat(JSON.stringify(usedReferenceValue, null, 2))); | ||
} | ||
if (!['string', 'number', 'object'].includes(usedReferenceValue === null || usedReferenceValue === void 0 ? void 0 : usedReferenceValue.type)) { | ||
throw new teleport_types_1.HTMLComponentGeneratorError("Attribute is using dynamic value, but received of type ".concat(JSON.stringify(usedReferenceValue, null, 2))); | ||
} | ||
return String(usedReferenceValue.defaultValue); | ||
return usedReferenceValue; | ||
}; | ||
//# sourceMappingURL=node-handlers.js.map |
@@ -1,2 +0,2 @@ | ||
export declare const DEFAULT_COMPONENT_CHUNK_NAME = "html-template"; | ||
export declare const DEFAULT_COMPONENT_CHUNK_NAME = "html-chunk"; | ||
//# sourceMappingURL=constants.d.ts.map |
@@ -1,2 +0,2 @@ | ||
export var DEFAULT_COMPONENT_CHUNK_NAME = 'html-template'; | ||
export var DEFAULT_COMPONENT_CHUNK_NAME = 'html-chunk'; | ||
//# sourceMappingURL=constants.js.map |
@@ -8,3 +8,3 @@ import { ComponentPlugin, ComponentDefaultPluginParams, ComponentUIDL } from '@teleporthq/teleport-types'; | ||
htmlComponentPlugin: ComponentPlugin; | ||
addExternals: (list: Record<string, ComponentUIDL>) => void; | ||
addExternals: (list: Record<string, ComponentUIDL>, plugins: ComponentPlugin[]) => void; | ||
} | ||
@@ -11,0 +11,0 @@ type HtmlPluginFactory<T> = (config?: Partial<T & ComponentDefaultPluginParams>) => HtmlPlugin; |
@@ -51,3 +51,3 @@ var __assign = (this && this.__assign) || function () { | ||
import { DEFAULT_COMPONENT_CHUNK_NAME } from './constants'; | ||
import { generateHtmlSynatx } from './node-handlers'; | ||
import { generateHtmlSyntax } from './node-handlers'; | ||
import { StringUtils, UIDLUtils } from '@teleporthq/teleport-shared'; | ||
@@ -57,17 +57,21 @@ export var createHTMLBasePlugin = function (config) { | ||
var externals = {}; | ||
var addExternals = function (list) { | ||
var plugins = []; | ||
var addExternals = function (list, subComponentPlugins) { | ||
if (subComponentPlugins === void 0) { subComponentPlugins = []; } | ||
externals = __assign(__assign({}, externals), (list || {})); | ||
plugins = subComponentPlugins; | ||
}; | ||
var htmlComponentPlugin = function (structure) { return __awaiter(void 0, void 0, void 0, function () { | ||
var uidl, chunks, dependencies, options, _a, propDefinitions, _b, stateDefinitions, templatesLookUp, compBase, bodyContent; | ||
return __generator(this, function (_c) { | ||
switch (_c.label) { | ||
var uidl, _a, chunks, dependencies, options, _b, propDefinitions, _c, stateDefinitions, outputOptions, nodesLookup, compBase, subComponents, templateOptions, _i, _d, propKey, prop, bodyContent; | ||
return __generator(this, function (_e) { | ||
switch (_e.label) { | ||
case 0: | ||
uidl = structure.uidl, chunks = structure.chunks, dependencies = structure.dependencies, options = structure.options; | ||
_a = uidl.propDefinitions, propDefinitions = _a === void 0 ? {} : _a, _b = uidl.stateDefinitions, stateDefinitions = _b === void 0 ? {} : _b; | ||
templatesLookUp = {}; | ||
uidl = structure.uidl, _a = structure.chunks, chunks = _a === void 0 ? [] : _a, dependencies = structure.dependencies, options = structure.options; | ||
_b = uidl.propDefinitions, propDefinitions = _b === void 0 ? {} : _b, _c = uidl.stateDefinitions, stateDefinitions = _c === void 0 ? {} : _c, outputOptions = uidl.outputOptions; | ||
nodesLookup = {}; | ||
compBase = wrapComponent | ||
? HASTBuilders.createHTMLNode('body') | ||
: HASTBuilders.createHTMLNode('div'); | ||
return [4 /*yield*/, generateHtmlSynatx(uidl.node, templatesLookUp, propDefinitions, stateDefinitions, Object.values(externals).reduce(function (acc, comp) { | ||
subComponents = { | ||
externals: Object.values(externals).reduce(function (acc, comp) { | ||
UIDLUtils.setFriendlyOutputOptions(comp); | ||
@@ -78,5 +82,25 @@ comp.name = StringUtils.removeIllegalCharacters(comp.name) || 'AppComponent'; | ||
return acc; | ||
}, {}), options.projectRouteDefinition, { chunks: chunks, dependencies: dependencies, options: options })]; | ||
}, {}), | ||
plugins: plugins, | ||
}; | ||
templateOptions = { chunks: chunks, dependencies: dependencies, options: options, outputOptions: outputOptions }; | ||
_i = 0, _d = Object.keys(propDefinitions); | ||
_e.label = 1; | ||
case 1: | ||
bodyContent = _c.sent(); | ||
if (!(_i < _d.length)) return [3 /*break*/, 4]; | ||
propKey = _d[_i]; | ||
prop = propDefinitions[propKey]; | ||
if (!(prop.type === 'element' && | ||
prop.defaultValue !== undefined && | ||
typeof prop.defaultValue === 'object')) return [3 /*break*/, 3]; | ||
return [4 /*yield*/, generateHtmlSyntax(prop.defaultValue, nodesLookup, propDefinitions, stateDefinitions, subComponents, templateOptions)]; | ||
case 2: | ||
_e.sent(); | ||
_e.label = 3; | ||
case 3: | ||
_i++; | ||
return [3 /*break*/, 1]; | ||
case 4: return [4 /*yield*/, generateHtmlSyntax(uidl.node, nodesLookup, propDefinitions, stateDefinitions, subComponents, templateOptions)]; | ||
case 5: | ||
bodyContent = _e.sent(); | ||
HASTUtils.addChildNode(compBase, bodyContent); | ||
@@ -90,3 +114,3 @@ chunks.push({ | ||
meta: { | ||
nodesLookup: templatesLookUp, | ||
nodesLookup: nodesLookup, | ||
}, | ||
@@ -93,0 +117,0 @@ }); |
@@ -1,9 +0,13 @@ | ||
import { UIDLNode, HastNode, UIDLPropDefinition, UIDLStateDefinition, HastText, ComponentUIDL, ChunkDefinition, UIDLDependency, GeneratorOptions, UIDLRouteDefinitions } from '@teleporthq/teleport-types'; | ||
type NodeToHTML<NodeType, ReturnType> = (node: NodeType, templatesLookUp: Record<string, unknown>, propDefinitions: Record<string, UIDLPropDefinition>, stateDefinitions: Record<string, UIDLStateDefinition>, externals: Record<string, ComponentUIDL>, routeDefinitions: UIDLRouteDefinitions, structure: { | ||
import { UIDLNode, HastNode, UIDLPropDefinition, UIDLStateDefinition, HastText, ComponentUIDL, ChunkDefinition, UIDLDependency, GeneratorOptions, ComponentPlugin, UIDLComponentOutputOptions } from '@teleporthq/teleport-types'; | ||
type NodeToHTML<NodeType, ReturnType> = (node: NodeType, nodesLookup: Record<string, HastNode | HastText>, propDefinitions: Record<string, UIDLPropDefinition>, stateDefinitions: Record<string, UIDLStateDefinition>, subComponentOptions: { | ||
externals: Record<string, ComponentUIDL>; | ||
plugins: ComponentPlugin[]; | ||
}, structure: { | ||
chunks: ChunkDefinition[]; | ||
dependencies: Record<string, UIDLDependency>; | ||
options: GeneratorOptions; | ||
outputOptions: UIDLComponentOutputOptions; | ||
}) => ReturnType; | ||
export declare const generateHtmlSynatx: NodeToHTML<UIDLNode, Promise<HastNode | HastText>>; | ||
export declare const generateHtmlSyntax: NodeToHTML<UIDLNode, Promise<HastNode | HastText>>; | ||
export {}; | ||
//# sourceMappingURL=node-handlers.d.ts.map |
@@ -58,2 +58,3 @@ var __assign = (this && this.__assign) || function () { | ||
import { HTMLComponentGeneratorError, ChunkType, FileType, } from '@teleporthq/teleport-types'; | ||
import { join, relative } from 'path'; | ||
import { HASTBuilders, HASTUtils } from '@teleporthq/teleport-plugin-common'; | ||
@@ -64,30 +65,48 @@ import { StringUtils, UIDLUtils } from '@teleporthq/teleport-shared'; | ||
import { DEFAULT_COMPONENT_CHUNK_NAME } from './constants'; | ||
export var generateHtmlSynatx = function (node, templatesLookUp, propDefinitions, stateDefinitions, externals, routeDefinitions, structure) { return __awaiter(void 0, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
switch (node.type) { | ||
case 'inject': | ||
case 'raw': | ||
return [2 /*return*/, HASTBuilders.createTextNode(node.content.toString())]; | ||
case 'static': | ||
return [2 /*return*/, HASTBuilders.createTextNode(StringUtils.encode(node.content.toString()))]; | ||
case 'slot': | ||
return [2 /*return*/, HASTBuilders.createHTMLNode(node.type)]; | ||
case 'element': | ||
return [2 /*return*/, generatElementNode(node, templatesLookUp, propDefinitions, stateDefinitions, externals, routeDefinitions, structure)]; | ||
case 'dynamic': | ||
return [2 /*return*/, generateDynamicNode(node, templatesLookUp, propDefinitions, stateDefinitions, externals, routeDefinitions, structure)]; | ||
default: | ||
throw new HTMLComponentGeneratorError("generateHtmlSyntax encountered a node of unsupported type: ".concat(JSON.stringify(node, null, 2), " ")); | ||
var isValidURL = function (url) { | ||
try { | ||
/* tslint:disable:no-unused-expression */ | ||
new URL(url); | ||
return true; | ||
} | ||
catch (error) { | ||
return false; | ||
} | ||
}; | ||
export var generateHtmlSyntax = function (node, nodesLookup, propDefinitions, stateDefinitions, subComponentOptions, structure) { return __awaiter(void 0, void 0, void 0, function () { | ||
var _a, elementNode, dynamicNode; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
_a = node.type; | ||
switch (_a) { | ||
case 'inject': return [3 /*break*/, 1]; | ||
case 'raw': return [3 /*break*/, 1]; | ||
case 'static': return [3 /*break*/, 2]; | ||
case 'slot': return [3 /*break*/, 3]; | ||
case 'element': return [3 /*break*/, 4]; | ||
case 'dynamic': return [3 /*break*/, 6]; | ||
} | ||
return [3 /*break*/, 8]; | ||
case 1: return [2 /*return*/, HASTBuilders.createTextNode(node.content.toString())]; | ||
case 2: return [2 /*return*/, HASTBuilders.createTextNode(StringUtils.encode(node.content.toString()))]; | ||
case 3: return [2 /*return*/, HASTBuilders.createHTMLNode(node.type)]; | ||
case 4: return [4 /*yield*/, generateElementNode(node, nodesLookup, propDefinitions, stateDefinitions, subComponentOptions, structure)]; | ||
case 5: | ||
elementNode = _b.sent(); | ||
return [2 /*return*/, elementNode]; | ||
case 6: return [4 /*yield*/, generateDynamicNode(node, nodesLookup, propDefinitions, stateDefinitions, subComponentOptions, structure)]; | ||
case 7: | ||
dynamicNode = _b.sent(); | ||
return [2 /*return*/, dynamicNode]; | ||
case 8: throw new HTMLComponentGeneratorError("generateHtmlSyntax encountered a node of unsupported type: ".concat(JSON.stringify(node, null, 2), " ")); | ||
} | ||
return [2 /*return*/]; | ||
}); | ||
}); }; | ||
var generatElementNode = function (node, templatesLookUp, propDefinitions, stateDefinitions, externals, routeDefinitions, structure) { return __awaiter(void 0, void 0, void 0, function () { | ||
var _a, elementType, children, _b, attrs, _c, style, _d, referencedStyles, dependency, key, elementNode, dependencies, compTag, _i, children_1, child, childTag; | ||
return __generator(this, function (_e) { | ||
switch (_e.label) { | ||
var generateElementNode = function (node, nodesLookup, propDefinitions, stateDefinitions, subComponentOptions, structure) { return __awaiter(void 0, void 0, void 0, function () { | ||
var _a, elementType, children, _b, attrs, _c, style, _d, referencedStyles, dependency, key, dependencies, _i, _e, attrKey, attr, compTag, elementNode, _f, children_1, child, childTag; | ||
return __generator(this, function (_g) { | ||
switch (_g.label) { | ||
case 0: | ||
_a = node.content, elementType = _a.elementType, children = _a.children, _b = _a.attrs, attrs = _b === void 0 ? {} : _b, _c = _a.style, style = _c === void 0 ? {} : _c, _d = _a.referencedStyles, referencedStyles = _d === void 0 ? {} : _d, dependency = _a.dependency, key = _a.key; | ||
elementNode = HASTBuilders.createHTMLNode(elementType); | ||
templatesLookUp[key] = elementNode; | ||
dependencies = structure.dependencies; | ||
@@ -97,20 +116,32 @@ if (dependency && (dependency === null || dependency === void 0 ? void 0 : dependency.type) !== 'local') { | ||
} | ||
if (!(dependency && (dependency === null || dependency === void 0 ? void 0 : dependency.type) === 'local')) return [3 /*break*/, 2]; | ||
return [4 /*yield*/, generateComponentContent(node, propDefinitions, stateDefinitions, externals, routeDefinitions, structure)]; | ||
if (!(dependency && (dependency === null || dependency === void 0 ? void 0 : dependency.type) === 'local')) return [3 /*break*/, 6]; | ||
_i = 0, _e = Object.keys(attrs); | ||
_g.label = 1; | ||
case 1: | ||
compTag = _e.sent(); | ||
return [2 /*return*/, compTag]; | ||
if (!(_i < _e.length)) return [3 /*break*/, 4]; | ||
attrKey = _e[_i]; | ||
attr = attrs[attrKey]; | ||
if (!(attr.type === 'element')) return [3 /*break*/, 3]; | ||
return [4 /*yield*/, generateElementNode(attr, nodesLookup, propDefinitions, stateDefinitions, subComponentOptions, structure)]; | ||
case 2: | ||
if (!children) return [3 /*break*/, 6]; | ||
_i = 0, children_1 = children; | ||
_e.label = 3; | ||
_g.sent(); | ||
_g.label = 3; | ||
case 3: | ||
if (!(_i < children_1.length)) return [3 /*break*/, 6]; | ||
child = children_1[_i]; | ||
return [4 /*yield*/, generateHtmlSynatx(child, templatesLookUp, propDefinitions, stateDefinitions, externals, routeDefinitions, structure)]; | ||
case 4: | ||
childTag = _e.sent(); | ||
if (!childTag) { | ||
return [2 /*return*/]; | ||
} | ||
_i++; | ||
return [3 /*break*/, 1]; | ||
case 4: return [4 /*yield*/, generateComponentContent(node, nodesLookup, propDefinitions, stateDefinitions, subComponentOptions, structure)]; | ||
case 5: | ||
compTag = _g.sent(); | ||
return [2 /*return*/, compTag]; | ||
case 6: | ||
elementNode = HASTBuilders.createHTMLNode(elementType); | ||
if (!children) return [3 /*break*/, 10]; | ||
_f = 0, children_1 = children; | ||
_g.label = 7; | ||
case 7: | ||
if (!(_f < children_1.length)) return [3 /*break*/, 10]; | ||
child = children_1[_f]; | ||
return [4 /*yield*/, generateHtmlSyntax(child, nodesLookup, propDefinitions, stateDefinitions, subComponentOptions, structure)]; | ||
case 8: | ||
childTag = _g.sent(); | ||
if (typeof childTag === 'string') { | ||
@@ -122,7 +153,7 @@ HASTUtils.addTextNode(elementNode, childTag); | ||
} | ||
_e.label = 5; | ||
case 5: | ||
_i++; | ||
return [3 /*break*/, 3]; | ||
case 6: | ||
_g.label = 9; | ||
case 9: | ||
_f++; | ||
return [3 /*break*/, 7]; | ||
case 10: | ||
if (Object.keys(referencedStyles).length > 0) { | ||
@@ -140,5 +171,4 @@ Object.keys(referencedStyles).forEach(function (styleRef) { | ||
} | ||
if (Object.keys(attrs).length > 0) { | ||
handleAttributes(elementNode, attrs, propDefinitions, stateDefinitions, routeDefinitions); | ||
} | ||
handleAttributes(elementType, elementNode, attrs, propDefinitions, stateDefinitions, structure.options.projectRouteDefinition, structure.outputOptions); | ||
nodesLookup[key] = elementNode; | ||
return [2 /*return*/, elementNode]; | ||
@@ -148,19 +178,21 @@ } | ||
}); }; | ||
var generateComponentContent = function (node, propDefinitions, stateDefinitions, externals, routeDefinitions, structure) { return __awaiter(void 0, void 0, void 0, function () { | ||
var _a, elementType, _b, attrs, key, _c, children, dependencies, chunks, options, comp, lookUpTemplates, compHasSlots, combinedProps, propsForInstance, combinedStates, statesForInstance, elementNode, compTag, cssPlugin, result, chunk, styleChunk; | ||
var _d; | ||
return __generator(this, function (_e) { | ||
switch (_e.label) { | ||
var generateComponentContent = function (node, nodesLookup, propDefinitions, stateDefinitions, subComponentOptions, structure) { return __awaiter(void 0, void 0, void 0, function () { | ||
var externals, plugins, _a, elementType, _b, attrs, key, _c, children, dependencies, _d, chunks, options, compName, component, componentClone, compHasSlots, combinedProps, propsForInstance, _i, _e, propKey, combinedStates, statesForInstance, componentWrapper, isExistingNode, componentInstanceToGenerate, compTag, cssPlugin, initialStructure, result, chunk, styleChunk; | ||
var _f, _g, _h; | ||
return __generator(this, function (_j) { | ||
switch (_j.label) { | ||
case 0: | ||
externals = subComponentOptions.externals, plugins = subComponentOptions.plugins; | ||
_a = node.content, elementType = _a.elementType, _b = _a.attrs, attrs = _b === void 0 ? {} : _b, key = _a.key, _c = _a.children, children = _c === void 0 ? [] : _c; | ||
dependencies = structure.dependencies, chunks = structure.chunks, options = structure.options; | ||
comp = UIDLUtils.cloneObject(externals[elementType] || {}); | ||
lookUpTemplates = {}; | ||
dependencies = structure.dependencies, _d = structure.chunks, chunks = _d === void 0 ? [] : _d, options = structure.options; | ||
compName = elementType === 'Component' ? 'AppComponent' : elementType; | ||
component = externals[compName]; | ||
if (component === undefined) { | ||
throw new HTMLComponentGeneratorError("".concat(compName, " is missing from externals object")); | ||
} | ||
componentClone = UIDLUtils.cloneObject(component); | ||
compHasSlots = false; | ||
if (!comp || !(comp === null || comp === void 0 ? void 0 : comp.node)) { | ||
throw new HTMLComponentGeneratorError("".concat(elementType, " is not found from the externals. \n\n Received ").concat(JSON.stringify(Object.keys(externals), null, 2))); | ||
} | ||
if (children.length) { | ||
compHasSlots = true; | ||
UIDLUtils.traverseNodes(comp.node, function (childNode, parentNode) { | ||
UIDLUtils.traverseNodes(componentClone.node, function (childNode, parentNode) { | ||
var _a, _b; | ||
@@ -193,14 +225,18 @@ if (childNode.type === 'slot' && parentNode.type === 'element') { | ||
} | ||
combinedProps = __assign(__assign({}, propDefinitions), ((comp === null || comp === void 0 ? void 0 : comp.propDefinitions) || {})); | ||
propsForInstance = Object.keys(combinedProps).reduce(function (acc, propKey) { | ||
var _a, _b; | ||
if (attrs[propKey]) { | ||
acc[propKey] = __assign(__assign({}, combinedProps[propKey]), { defaultValue: ((_a = attrs[propKey]) === null || _a === void 0 ? void 0 : _a.content) || ((_b = combinedProps[propKey]) === null || _b === void 0 ? void 0 : _b.defaultValue) }); | ||
combinedProps = __assign(__assign({}, propDefinitions), ((componentClone === null || componentClone === void 0 ? void 0 : componentClone.propDefinitions) || {})); | ||
propsForInstance = {}; | ||
for (_i = 0, _e = Object.keys(combinedProps); _i < _e.length; _i++) { | ||
propKey = _e[_i]; | ||
// If the attribute is a named-slot, then we can directly pass the value instead of just the content | ||
if (((_f = attrs[propKey]) === null || _f === void 0 ? void 0 : _f.type) === 'element') { | ||
propsForInstance[propKey] = __assign(__assign({}, combinedProps[propKey]), { defaultValue: attrs[propKey] }); | ||
} | ||
else if (attrs[propKey]) { | ||
propsForInstance[propKey] = __assign(__assign({}, combinedProps[propKey]), { defaultValue: ((_g = attrs[propKey]) === null || _g === void 0 ? void 0 : _g.content) || ((_h = combinedProps[propKey]) === null || _h === void 0 ? void 0 : _h.defaultValue) }); | ||
} | ||
else { | ||
acc[propKey] = combinedProps[propKey]; | ||
propsForInstance[propKey] = combinedProps[propKey]; | ||
} | ||
return acc; | ||
}, {}); | ||
combinedStates = __assign(__assign({}, stateDefinitions), ((comp === null || comp === void 0 ? void 0 : comp.stateDefinitions) || {})); | ||
} | ||
combinedStates = __assign(__assign({}, stateDefinitions), ((componentClone === null || componentClone === void 0 ? void 0 : componentClone.stateDefinitions) || {})); | ||
statesForInstance = Object.keys(combinedStates).reduce(function (acc, propKey) { | ||
@@ -216,37 +252,62 @@ var _a, _b; | ||
}, {}); | ||
elementNode = HASTBuilders.createHTMLNode(StringUtils.camelCaseToDashCase(elementType)); | ||
lookUpTemplates[key] = elementNode; | ||
return [4 /*yield*/, generateHtmlSynatx(__assign(__assign({}, comp.node), { content: __assign(__assign({}, comp.node.content), { style: __assign(__assign({}, (((_d = comp.node.content) === null || _d === void 0 ? void 0 : _d.style) || {})), { display: { | ||
type: 'static', | ||
content: 'contents', | ||
} }) }) }), lookUpTemplates, propsForInstance, statesForInstance, externals, routeDefinitions, structure)]; | ||
componentWrapper = StringUtils.camelCaseToDashCase("".concat(compName, "-wrapper")); | ||
isExistingNode = nodesLookup[componentWrapper]; | ||
if (isExistingNode !== undefined) { | ||
componentWrapper = "".concat(componentWrapper, "-").concat(StringUtils.generateRandomString()); | ||
} | ||
componentInstanceToGenerate = { | ||
type: 'element', | ||
content: { | ||
elementType: componentWrapper, | ||
key: componentWrapper, | ||
children: [componentClone.node], | ||
style: { | ||
display: { | ||
type: 'static', | ||
content: 'contents', | ||
}, | ||
}, | ||
}, | ||
}; | ||
return [4 /*yield*/, generateHtmlSyntax(componentInstanceToGenerate, nodesLookup, propsForInstance, statesForInstance, subComponentOptions, structure)]; | ||
case 1: | ||
compTag = (_e.sent()); | ||
compTag = _j.sent(); | ||
cssPlugin = createCSSPlugin({ | ||
templateStyle: 'html', | ||
templateChunkName: 'html-template', | ||
templateChunkName: DEFAULT_COMPONENT_CHUNK_NAME, | ||
declareDependency: 'import', | ||
forceScoping: true, | ||
chunkName: comp.name, | ||
chunkName: componentClone.name, | ||
staticPropReferences: true, | ||
}); | ||
return [4 /*yield*/, cssPlugin({ | ||
uidl: __assign(__assign({}, comp), { propDefinitions: propsForInstance, stateDefinitions: statesForInstance }), | ||
chunks: [ | ||
{ | ||
type: ChunkType.HAST, | ||
fileType: FileType.HTML, | ||
name: DEFAULT_COMPONENT_CHUNK_NAME, | ||
linkAfter: [], | ||
content: compTag, | ||
meta: { | ||
nodesLookup: lookUpTemplates, | ||
}, | ||
initialStructure = { | ||
uidl: __assign(__assign({}, componentClone), { propDefinitions: propsForInstance, stateDefinitions: statesForInstance }), | ||
chunks: [ | ||
{ | ||
type: ChunkType.HAST, | ||
fileType: FileType.HTML, | ||
name: DEFAULT_COMPONENT_CHUNK_NAME, | ||
linkAfter: [], | ||
content: compTag, | ||
meta: { | ||
nodesLookup: nodesLookup, | ||
}, | ||
], | ||
dependencies: dependencies, | ||
options: options, | ||
})]; | ||
}, | ||
], | ||
dependencies: dependencies, | ||
options: options, | ||
}; | ||
return [4 /*yield*/, __spreadArray([cssPlugin], plugins, true).reduce(function (previousPluginOperation, plugin) { return __awaiter(void 0, void 0, void 0, function () { | ||
var modifiedStructure; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, previousPluginOperation]; | ||
case 1: | ||
modifiedStructure = _a.sent(); | ||
return [2 /*return*/, plugin(modifiedStructure)]; | ||
} | ||
}); | ||
}); }, Promise.resolve(initialStructure))]; | ||
case 2: | ||
result = _e.sent(); | ||
result = _j.sent(); | ||
if (compHasSlots) { | ||
@@ -260,8 +321,12 @@ result.chunks.forEach(function (chunk) { | ||
else { | ||
chunk = chunks.find(function (item) { return item.name === comp.name; }); | ||
chunk = chunks.find(function (item) { return item.name === componentClone.name; }); | ||
if (!chunk) { | ||
styleChunk = result.chunks.find(function (item) { return item.name === comp.name; }); | ||
styleChunk = result.chunks.find(function (item) { return item.fileType === FileType.CSS; }); | ||
if (!styleChunk) { | ||
return [2 /*return*/]; | ||
} | ||
chunks.push(styleChunk); | ||
} | ||
} | ||
nodesLookup[key] = compTag; | ||
return [2 /*return*/, compTag]; | ||
@@ -271,10 +336,26 @@ } | ||
}); }; | ||
var generateDynamicNode = function (node, _, propDefinitions, stateDefinitions) { | ||
var spanTag = HASTBuilders.createHTMLNode('span'); | ||
var usedReferenceValue = node.content.referenceType === 'prop' | ||
? getValueFromReference(node.content.id, propDefinitions) | ||
: getValueFromReference(node.content.id, stateDefinitions); | ||
HASTUtils.addTextNode(spanTag, String(usedReferenceValue)); | ||
return spanTag; | ||
}; | ||
var generateDynamicNode = function (node, nodesLookup, propDefinitions, stateDefinitions, subComponentOptions, structure) { return __awaiter(void 0, void 0, void 0, function () { | ||
var usedReferenceValue, slotNode, spanTagWrapper, commentNode, spanTag; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
usedReferenceValue = getValueFromReference(node.content.id, node.content.referenceType === 'prop' ? propDefinitions : stateDefinitions); | ||
if (!(usedReferenceValue.type === 'element' && usedReferenceValue.defaultValue)) return [3 /*break*/, 2]; | ||
return [4 /*yield*/, generateElementNode(usedReferenceValue.defaultValue, nodesLookup, propDefinitions, stateDefinitions, subComponentOptions, structure)]; | ||
case 1: | ||
slotNode = _a.sent(); | ||
return [2 /*return*/, slotNode]; | ||
case 2: | ||
if (usedReferenceValue.type === 'element' && usedReferenceValue.defaultValue === undefined) { | ||
spanTagWrapper = HASTBuilders.createHTMLNode('span'); | ||
commentNode = HASTBuilders.createComment("Content for slot ".concat(node.content.id)); | ||
HASTUtils.addChildNode(spanTagWrapper, commentNode); | ||
return [2 /*return*/, spanTagWrapper]; | ||
} | ||
spanTag = HASTBuilders.createHTMLNode('span'); | ||
HASTUtils.addTextNode(spanTag, String(usedReferenceValue.defaultValue)); | ||
return [2 /*return*/, spanTag]; | ||
} | ||
}); | ||
}); }; | ||
var handleStyles = function (node, styles, propDefinitions, stateDefinitions) { | ||
@@ -285,8 +366,6 @@ Object.keys(styles).forEach(function (styleKey) { | ||
if (style.type === 'dynamic' && ((_a = style.content) === null || _a === void 0 ? void 0 : _a.referenceType) !== 'token') { | ||
if (style.content.referenceType === 'prop') { | ||
style = getValueFromReference(style.content.id, propDefinitions); | ||
var referencedValue = getValueFromReference(style.content.id, style.content.referenceType === 'prop' ? propDefinitions : stateDefinitions); | ||
if (referencedValue.type === 'string' || referencedValue.type === 'number') { | ||
style = String(referencedValue.defaultValue); | ||
} | ||
else if (style.content.referenceType === 'state') { | ||
style = getValueFromReference(style.content.id, stateDefinitions); | ||
} | ||
node.content.style[styleKey] = typeof style === 'string' ? staticNode(style) : style; | ||
@@ -296,38 +375,81 @@ } | ||
}; | ||
var handleAttributes = function (htmlNode, attrs, propDefinitions, stateDefinitions, routeDefinitions) { | ||
Object.keys(attrs).forEach(function (attrKey) { | ||
var handleAttributes = function (elementType, htmlNode, attrs, propDefinitions, stateDefinitions, routeDefinitions, outputOptions) { | ||
var _loop_1 = function (attrKey) { | ||
var attrValue = attrs[attrKey]; | ||
if (attrKey === 'href' && | ||
attrValue.type === 'static' && | ||
typeof attrValue.content === 'string' && | ||
attrValue.content.startsWith('/')) { | ||
attrValue = | ||
attrValue.content === '/' || | ||
attrValue.content === | ||
"/".concat(StringUtils.camelCaseToDashCase(StringUtils.removeIllegalCharacters((routeDefinitions === null || routeDefinitions === void 0 ? void 0 : routeDefinitions.defaultValue) || ''))) | ||
? staticNode('index.html') | ||
: staticNode("".concat(attrValue.content.split('/').pop(), ".html")); | ||
HASTUtils.addAttributeToNode(htmlNode, attrKey, String(attrValue.content)); | ||
return; | ||
var type = attrValue.type, content = attrValue.content; | ||
switch (type) { | ||
case 'static': { | ||
if (attrKey === 'href' && typeof content === 'string' && content.startsWith('/')) { | ||
var targetLink = void 0; | ||
var targetRoute = ((routeDefinitions === null || routeDefinitions === void 0 ? void 0 : routeDefinitions.values) || []).find(function (route) { return route.pageOptions.navLink === content; }); | ||
if (targetRoute) { | ||
targetLink = targetRoute.pageOptions.navLink; | ||
} | ||
if (!targetRoute && content === '/home') { | ||
targetLink = '/'; | ||
} | ||
if (!targetLink && !targetRoute) { | ||
targetLink = content; | ||
} | ||
var currentPageRoute = join.apply(void 0, __spreadArray(__spreadArray([], ((outputOptions === null || outputOptions === void 0 ? void 0 : outputOptions.folderPath) || []), false), ['./'], false)); | ||
var localPrefix = relative("/".concat(currentPageRoute), "/".concat(targetLink === '/' ? 'index' : targetLink)); | ||
HASTUtils.addAttributeToNode(htmlNode, attrKey, "".concat(localPrefix, ".html")); | ||
break; | ||
} | ||
if (typeof content === 'boolean') { | ||
htmlNode.properties[attrKey] = content === true ? 'true' : 'false'; | ||
} | ||
else if (typeof content === 'string' || typeof attrValue.content === 'number') { | ||
var value = StringUtils.encode(String(attrValue.content)); | ||
/* | ||
elementType of image is always mapped to img. | ||
For reference, check `html-mapping` file. | ||
*/ | ||
if (elementType === 'img' && attrKey === 'src' && !isValidURL(value)) { | ||
/* | ||
By default we just prefix all the asset paths with just the | ||
assetPrefix that is configured in the project. But for `html` generators | ||
we need to prefix that with the current file location. | ||
Because, all the other frameworks have a build setup. which serves all the | ||
assets from the `public` folder. But in the case of `html` here is how it works | ||
We load a file from `index.html` the request for the image goes from | ||
'...url.../public/...image...' | ||
If it's a nested url, then the request goes from | ||
'...url/nested/public/...image..' | ||
But the nested folder is available only on the root. With this | ||
The url changes prefixes to | ||
../public/playground_assets/..image.. etc depending on the dept the file is in. | ||
*/ | ||
value = join(relative(join.apply(void 0, outputOptions.folderPath), './'), value); | ||
} | ||
HASTUtils.addAttributeToNode(htmlNode, attrKey, value); | ||
} | ||
break; | ||
} | ||
case 'dynamic': { | ||
var value = getValueFromReference(content.id, content.referenceType === 'prop' ? propDefinitions : stateDefinitions); | ||
HASTUtils.addAttributeToNode(htmlNode, attrKey, String(value.defaultValue)); | ||
break; | ||
} | ||
case 'raw': { | ||
HASTUtils.addAttributeToNode(htmlNode, attrKey, content); | ||
break; | ||
} | ||
case 'element': | ||
case 'import': | ||
case 'expr': | ||
break; | ||
default: { | ||
throw new HTMLComponentGeneratorError("Received ".concat(JSON.stringify(attrValue, null, 2), " \n in handleAttributes for html")); | ||
} | ||
} | ||
if (attrValue.type === 'dynamic') { | ||
var value = attrValue.content.referenceType === 'prop' | ||
? getValueFromReference(attrValue.content.id, propDefinitions) | ||
: getValueFromReference(attrValue.content.id, stateDefinitions); | ||
HASTUtils.addAttributeToNode(htmlNode, attrKey, String(value)); | ||
return; | ||
} | ||
if (attrValue.type === 'raw') { | ||
HASTUtils.addAttributeToNode(htmlNode, attrKey, attrValue.content); | ||
return; | ||
} | ||
if (typeof attrValue.content === 'boolean') { | ||
HASTUtils.addBooleanAttributeToNode(htmlNode, attrKey); | ||
return; | ||
} | ||
else if (typeof attrValue.content === 'string' || typeof attrValue.content === 'number') { | ||
HASTUtils.addAttributeToNode(htmlNode, attrKey, StringUtils.encode(String(attrValue.content))); | ||
return; | ||
} | ||
}); | ||
}; | ||
for (var _i = 0, _a = Object.keys(attrs); _i < _a.length; _i++) { | ||
var attrKey = _a[_i]; | ||
_loop_1(attrKey); | ||
} | ||
}; | ||
@@ -339,10 +461,11 @@ var getValueFromReference = function (key, definitions) { | ||
} | ||
if (!usedReferenceValue.hasOwnProperty('defaultValue')) { | ||
if (['string', 'number', 'object', 'element'].includes(usedReferenceValue === null || usedReferenceValue === void 0 ? void 0 : usedReferenceValue.type) === false) { | ||
throw new HTMLComponentGeneratorError("Attribute is using dynamic value, but received of type ".concat(JSON.stringify(usedReferenceValue, null, 2))); | ||
} | ||
if (usedReferenceValue.type !== 'element' && | ||
usedReferenceValue.hasOwnProperty('defaultValue') === false) { | ||
throw new HTMLComponentGeneratorError("Default value is missing from dynamic reference - ".concat(JSON.stringify(usedReferenceValue, null, 2))); | ||
} | ||
if (!['string', 'number', 'object'].includes(usedReferenceValue === null || usedReferenceValue === void 0 ? void 0 : usedReferenceValue.type)) { | ||
throw new HTMLComponentGeneratorError("Attribute is using dynamic value, but received of type ".concat(JSON.stringify(usedReferenceValue, null, 2))); | ||
} | ||
return String(usedReferenceValue.defaultValue); | ||
return usedReferenceValue; | ||
}; | ||
//# sourceMappingURL=node-handlers.js.map |
{ | ||
"name": "@teleporthq/teleport-plugin-html-base-component", | ||
"version": "0.34.0-alpha.0", | ||
"version": "0.34.0", | ||
"description": "A plugin for handling the skeleton/baseline of a base html component", | ||
@@ -27,9 +27,9 @@ "author": "teleportHQ", | ||
"dependencies": { | ||
"@teleporthq/teleport-plugin-common": "^0.34.0-alpha.0", | ||
"@teleporthq/teleport-plugin-css": "^0.34.0-alpha.0", | ||
"@teleporthq/teleport-shared": "^0.34.0-alpha.0", | ||
"@teleporthq/teleport-types": "^0.34.0-alpha.0", | ||
"@teleporthq/teleport-uidl-builders": "^0.34.0-alpha.0" | ||
"@teleporthq/teleport-plugin-common": "^0.34.0", | ||
"@teleporthq/teleport-plugin-css": "^0.34.0", | ||
"@teleporthq/teleport-shared": "^0.34.0", | ||
"@teleporthq/teleport-types": "^0.34.0", | ||
"@teleporthq/teleport-uidl-builders": "^0.34.0" | ||
}, | ||
"gitHead": "89e99b8d79cd3895febd6ba5cd26499c8e743e16" | ||
"gitHead": "e96dd1dda3eebc0a845b2ede19f057702170f0d8" | ||
} |
@@ -1,1 +0,1 @@ | ||
export const DEFAULT_COMPONENT_CHUNK_NAME = 'html-template' | ||
export const DEFAULT_COMPONENT_CHUNK_NAME = 'html-chunk' |
@@ -8,6 +8,8 @@ import { | ||
ComponentUIDL, | ||
HastText, | ||
UIDLElementNode, | ||
} from '@teleporthq/teleport-types' | ||
import { HASTBuilders, HASTUtils } from '@teleporthq/teleport-plugin-common' | ||
import { DEFAULT_COMPONENT_CHUNK_NAME } from './constants' | ||
import { generateHtmlSynatx } from './node-handlers' | ||
import { generateHtmlSyntax } from './node-handlers' | ||
import { StringUtils, UIDLUtils } from '@teleporthq/teleport-shared' | ||
@@ -22,3 +24,3 @@ | ||
htmlComponentPlugin: ComponentPlugin | ||
addExternals: (list: Record<string, ComponentUIDL>) => void | ||
addExternals: (list: Record<string, ComponentUIDL>, plugins: ComponentPlugin[]) => void | ||
} | ||
@@ -31,4 +33,8 @@ | ||
let externals: Record<string, ComponentUIDL> = {} | ||
let plugins: ComponentPlugin[] = [] | ||
const addExternals = (list?: Record<string, ComponentUIDL>) => { | ||
const addExternals = ( | ||
list?: Record<string, ComponentUIDL>, | ||
subComponentPlugins: ComponentPlugin[] = [] | ||
) => { | ||
externals = { | ||
@@ -38,9 +44,10 @@ ...externals, | ||
} | ||
plugins = subComponentPlugins | ||
} | ||
const htmlComponentPlugin: ComponentPlugin = async (structure) => { | ||
const { uidl, chunks, dependencies, options } = structure | ||
const { propDefinitions = {}, stateDefinitions = {} } = uidl | ||
const { uidl, chunks = [], dependencies, options } = structure | ||
const { propDefinitions = {}, stateDefinitions = {}, outputOptions } = uidl | ||
const templatesLookUp: Record<string, unknown> = {} | ||
const nodesLookup: Record<string, HastNode | HastText> = {} | ||
const compBase = wrapComponent | ||
@@ -50,17 +57,48 @@ ? HASTBuilders.createHTMLNode('body') | ||
const bodyContent = await generateHtmlSynatx( | ||
const subComponents = { | ||
externals: Object.values(externals).reduce( | ||
(acc: Record<string, ComponentUIDL>, comp: ComponentUIDL) => { | ||
UIDLUtils.setFriendlyOutputOptions(comp) | ||
comp.name = StringUtils.removeIllegalCharacters(comp.name) || 'AppComponent' | ||
comp.name = UIDLUtils.getComponentClassName(comp) | ||
acc[comp.name] = comp | ||
return acc | ||
}, | ||
{} | ||
), | ||
plugins, | ||
} | ||
const templateOptions = { chunks, dependencies, options, outputOptions } | ||
/* | ||
We need to generate jsx structure of every node that is defined in the UIDL. | ||
If we use these nodes in the later stage of the code-generation depends on the usage of these nodes. | ||
*/ | ||
for (const propKey of Object.keys(propDefinitions)) { | ||
const prop = propDefinitions[propKey] | ||
if ( | ||
prop.type === 'element' && | ||
prop.defaultValue !== undefined && | ||
typeof prop.defaultValue === 'object' | ||
) { | ||
await generateHtmlSyntax( | ||
prop.defaultValue as UIDLElementNode, | ||
nodesLookup, | ||
propDefinitions, | ||
stateDefinitions, | ||
subComponents, | ||
templateOptions | ||
) | ||
} | ||
} | ||
const bodyContent = await generateHtmlSyntax( | ||
uidl.node, | ||
templatesLookUp, | ||
nodesLookup, | ||
propDefinitions, | ||
stateDefinitions, | ||
Object.values(externals).reduce((acc: Record<string, ComponentUIDL>, comp: ComponentUIDL) => { | ||
UIDLUtils.setFriendlyOutputOptions(comp) | ||
comp.name = StringUtils.removeIllegalCharacters(comp.name) || 'AppComponent' | ||
comp.name = UIDLUtils.getComponentClassName(comp) | ||
acc[comp.name] = comp | ||
return acc | ||
}, {}), | ||
options.projectRouteDefinition, | ||
{ chunks, dependencies, options } | ||
subComponents, | ||
templateOptions | ||
) | ||
HASTUtils.addChildNode(compBase, bodyContent as HastNode) | ||
@@ -75,3 +113,3 @@ | ||
meta: { | ||
nodesLookup: templatesLookUp, | ||
nodesLookup, | ||
}, | ||
@@ -78,0 +116,0 @@ }) |
@@ -20,3 +20,8 @@ import { | ||
UIDLRouteDefinitions, | ||
ComponentPlugin, | ||
ComponentStructure, | ||
UIDLComponentOutputOptions, | ||
UIDLElement, | ||
} from '@teleporthq/teleport-types' | ||
import { join, relative } from 'path' | ||
import { HASTBuilders, HASTUtils } from '@teleporthq/teleport-plugin-common' | ||
@@ -28,9 +33,21 @@ import { StringUtils, UIDLUtils } from '@teleporthq/teleport-shared' | ||
const isValidURL = (url: string) => { | ||
try { | ||
/* tslint:disable:no-unused-expression */ | ||
new URL(url) | ||
return true | ||
} catch (error) { | ||
return false | ||
} | ||
} | ||
type NodeToHTML<NodeType, ReturnType> = ( | ||
node: NodeType, | ||
templatesLookUp: Record<string, unknown>, | ||
nodesLookup: Record<string, HastNode | HastText>, | ||
propDefinitions: Record<string, UIDLPropDefinition>, | ||
stateDefinitions: Record<string, UIDLStateDefinition>, | ||
externals: Record<string, ComponentUIDL>, | ||
routeDefinitions: UIDLRouteDefinitions, | ||
subComponentOptions: { | ||
externals: Record<string, ComponentUIDL> | ||
plugins: ComponentPlugin[] | ||
}, | ||
structure: { | ||
@@ -40,12 +57,12 @@ chunks: ChunkDefinition[] | ||
options: GeneratorOptions | ||
outputOptions: UIDLComponentOutputOptions | ||
} | ||
) => ReturnType | ||
export const generateHtmlSynatx: NodeToHTML<UIDLNode, Promise<HastNode | HastText>> = async ( | ||
export const generateHtmlSyntax: NodeToHTML<UIDLNode, Promise<HastNode | HastText>> = async ( | ||
node, | ||
templatesLookUp, | ||
nodesLookup, | ||
propDefinitions, | ||
stateDefinitions, | ||
externals, | ||
routeDefinitions, | ||
subComponentOptions, | ||
structure | ||
@@ -65,23 +82,24 @@ ) => { | ||
case 'element': | ||
return generatElementNode( | ||
const elementNode = await generateElementNode( | ||
node, | ||
templatesLookUp, | ||
nodesLookup, | ||
propDefinitions, | ||
stateDefinitions, | ||
externals, | ||
routeDefinitions, | ||
subComponentOptions, | ||
structure | ||
) | ||
return elementNode | ||
case 'dynamic': | ||
return generateDynamicNode( | ||
const dynamicNode = await generateDynamicNode( | ||
node, | ||
templatesLookUp, | ||
nodesLookup, | ||
propDefinitions, | ||
stateDefinitions, | ||
externals, | ||
routeDefinitions, | ||
subComponentOptions, | ||
structure | ||
) | ||
return dynamicNode | ||
default: | ||
@@ -98,9 +116,8 @@ throw new HTMLComponentGeneratorError( | ||
const generatElementNode: NodeToHTML<UIDLElementNode, Promise<HastNode | HastText>> = async ( | ||
const generateElementNode: NodeToHTML<UIDLElementNode, Promise<HastNode | HastText>> = async ( | ||
node, | ||
templatesLookUp, | ||
nodesLookup, | ||
propDefinitions, | ||
stateDefinitions, | ||
externals, | ||
routeDefinitions, | ||
subComponentOptions, | ||
structure | ||
@@ -117,6 +134,2 @@ ) => { | ||
} = node.content | ||
const elementNode = HASTBuilders.createHTMLNode(elementType) | ||
templatesLookUp[key] = elementNode | ||
const { dependencies } = structure | ||
@@ -128,29 +141,41 @@ if (dependency && (dependency as UIDLDependency)?.type !== 'local') { | ||
if (dependency && (dependency as UIDLDependency)?.type === 'local') { | ||
for (const attrKey of Object.keys(attrs)) { | ||
const attr = attrs[attrKey] | ||
if (attr.type === 'element') { | ||
await generateElementNode( | ||
attr, | ||
nodesLookup, | ||
propDefinitions, | ||
stateDefinitions, | ||
subComponentOptions, | ||
structure | ||
) | ||
} | ||
} | ||
const compTag = await generateComponentContent( | ||
node, | ||
nodesLookup, | ||
propDefinitions, | ||
stateDefinitions, | ||
externals, | ||
routeDefinitions, | ||
subComponentOptions, | ||
structure | ||
) | ||
return compTag | ||
} | ||
const elementNode = HASTBuilders.createHTMLNode(elementType) | ||
if (children) { | ||
for (const child of children) { | ||
const childTag = await generateHtmlSynatx( | ||
const childTag = await generateHtmlSyntax( | ||
child, | ||
templatesLookUp, | ||
nodesLookup, | ||
propDefinitions, | ||
stateDefinitions, | ||
externals, | ||
routeDefinitions, | ||
subComponentOptions, | ||
structure | ||
) | ||
if (!childTag) { | ||
return | ||
} | ||
if (typeof childTag === 'string') { | ||
@@ -178,6 +203,13 @@ HASTUtils.addTextNode(elementNode, childTag) | ||
if (Object.keys(attrs).length > 0) { | ||
handleAttributes(elementNode, attrs, propDefinitions, stateDefinitions, routeDefinitions) | ||
} | ||
handleAttributes( | ||
elementType, | ||
elementNode, | ||
attrs, | ||
propDefinitions, | ||
stateDefinitions, | ||
structure.options.projectRouteDefinition, | ||
structure.outputOptions | ||
) | ||
nodesLookup[key] = elementNode | ||
return elementNode | ||
@@ -188,6 +220,9 @@ } | ||
node: UIDLElementNode, | ||
nodesLookup: Record<string, HastNode | HastText>, | ||
propDefinitions: Record<string, UIDLPropDefinition>, | ||
stateDefinitions: Record<string, UIDLStateDefinition>, | ||
externals: Record<string, ComponentUIDL>, | ||
routeDefinitions: UIDLRouteDefinitions, | ||
subComponentOptions: { | ||
externals: Record<string, ComponentUIDL> | ||
plugins: ComponentPlugin[] | ||
}, | ||
structure: { | ||
@@ -197,18 +232,21 @@ chunks: ChunkDefinition[] | ||
options: GeneratorOptions | ||
outputOptions: UIDLComponentOutputOptions | ||
} | ||
) => { | ||
const { externals, plugins } = subComponentOptions | ||
const { elementType, attrs = {}, key, children = [] } = node.content | ||
const { dependencies, chunks, options } = structure | ||
const comp = UIDLUtils.cloneObject(externals[elementType] || {}) as ComponentUIDL | ||
const lookUpTemplates: Record<string, unknown> = {} | ||
const { dependencies, chunks = [], options } = structure | ||
// "Component" will not exist when generating a component because the resolver checks for illegal class names | ||
const compName = elementType === 'Component' ? 'AppComponent' : elementType | ||
const component = externals[compName] | ||
if (component === undefined) { | ||
throw new HTMLComponentGeneratorError(`${compName} is missing from externals object`) | ||
} | ||
const componentClone = UIDLUtils.cloneObject(component) as ComponentUIDL | ||
let compHasSlots: boolean = false | ||
if (!comp || !comp?.node) { | ||
throw new HTMLComponentGeneratorError(`${elementType} is not found from the externals. \n | ||
Received ${JSON.stringify(Object.keys(externals), null, 2)}`) | ||
} | ||
if (children.length) { | ||
compHasSlots = true | ||
UIDLUtils.traverseNodes(comp.node, (childNode, parentNode) => { | ||
UIDLUtils.traverseNodes(componentClone.node, (childNode, parentNode) => { | ||
if (childNode.type === 'slot' && parentNode.type === 'element') { | ||
@@ -242,21 +280,23 @@ const nonSlotNodes = parentNode.content?.children?.filter((n) => n.type !== 'slot') | ||
const combinedProps = { ...propDefinitions, ...(comp?.propDefinitions || {}) } | ||
const combinedProps = { ...propDefinitions, ...(componentClone?.propDefinitions || {}) } | ||
const propsForInstance: Record<string, UIDLPropDefinition> = {} | ||
const propsForInstance = Object.keys(combinedProps).reduce( | ||
(acc: Record<string, UIDLPropDefinition>, propKey) => { | ||
if (attrs[propKey]) { | ||
acc[propKey] = { | ||
...combinedProps[propKey], | ||
defaultValue: attrs[propKey]?.content || combinedProps[propKey]?.defaultValue, | ||
} | ||
} else { | ||
acc[propKey] = combinedProps[propKey] | ||
for (const propKey of Object.keys(combinedProps)) { | ||
// If the attribute is a named-slot, then we can directly pass the value instead of just the content | ||
if (attrs[propKey]?.type === 'element') { | ||
propsForInstance[propKey] = { | ||
...combinedProps[propKey], | ||
defaultValue: attrs[propKey], | ||
} | ||
} else if (attrs[propKey]) { | ||
propsForInstance[propKey] = { | ||
...combinedProps[propKey], | ||
defaultValue: attrs[propKey]?.content || combinedProps[propKey]?.defaultValue, | ||
} | ||
} else { | ||
propsForInstance[propKey] = combinedProps[propKey] | ||
} | ||
} | ||
return acc | ||
}, | ||
{} | ||
) | ||
const combinedStates = { ...stateDefinitions, ...(comp?.stateDefinitions || {}) } | ||
const combinedStates = { ...stateDefinitions, ...(componentClone?.stateDefinitions || {}) } | ||
const statesForInstance = Object.keys(combinedStates).reduce( | ||
@@ -278,39 +318,44 @@ (acc: Record<string, UIDLStateDefinition>, propKey) => { | ||
const elementNode = HASTBuilders.createHTMLNode(StringUtils.camelCaseToDashCase(elementType)) | ||
lookUpTemplates[key] = elementNode | ||
let componentWrapper = StringUtils.camelCaseToDashCase(`${compName}-wrapper`) | ||
const isExistingNode = nodesLookup[componentWrapper] | ||
if (isExistingNode !== undefined) { | ||
componentWrapper = `${componentWrapper}-${StringUtils.generateRandomString()}` | ||
} | ||
const compTag = (await generateHtmlSynatx( | ||
{ | ||
...comp.node, | ||
content: { | ||
...comp.node.content, | ||
style: { | ||
...(comp.node.content?.style || {}), | ||
display: { | ||
type: 'static', | ||
content: 'contents', | ||
}, | ||
const componentInstanceToGenerate: UIDLElementNode = { | ||
type: 'element', | ||
content: { | ||
elementType: componentWrapper, | ||
key: componentWrapper, | ||
children: [componentClone.node], | ||
style: { | ||
display: { | ||
type: 'static', | ||
content: 'contents', | ||
}, | ||
}, | ||
}, | ||
lookUpTemplates, | ||
} | ||
const compTag = await generateHtmlSyntax( | ||
componentInstanceToGenerate, | ||
nodesLookup, | ||
propsForInstance, | ||
statesForInstance, | ||
externals, | ||
routeDefinitions, | ||
subComponentOptions, | ||
structure | ||
)) as unknown as HastNode | ||
) | ||
const cssPlugin = createCSSPlugin({ | ||
templateStyle: 'html', | ||
templateChunkName: 'html-template', | ||
templateChunkName: DEFAULT_COMPONENT_CHUNK_NAME, | ||
declareDependency: 'import', | ||
forceScoping: true, | ||
chunkName: comp.name, | ||
chunkName: componentClone.name, | ||
staticPropReferences: true, | ||
}) | ||
const result = await cssPlugin({ | ||
const initialStructure: ComponentStructure = { | ||
uidl: { | ||
...comp, | ||
...componentClone, | ||
propDefinitions: propsForInstance, | ||
@@ -327,3 +372,3 @@ stateDefinitions: statesForInstance, | ||
meta: { | ||
nodesLookup: lookUpTemplates, | ||
nodesLookup, | ||
}, | ||
@@ -334,4 +379,12 @@ }, | ||
options, | ||
}) | ||
} | ||
const result = await [cssPlugin, ...plugins].reduce( | ||
async (previousPluginOperation: Promise<ComponentStructure>, plugin) => { | ||
const modifiedStructure = await previousPluginOperation | ||
return plugin(modifiedStructure) | ||
}, | ||
Promise.resolve(initialStructure) | ||
) | ||
if (compHasSlots) { | ||
@@ -344,5 +397,10 @@ result.chunks.forEach((chunk) => { | ||
} else { | ||
const chunk = chunks.find((item) => item.name === comp.name) | ||
const chunk = chunks.find((item) => item.name === componentClone.name) | ||
if (!chunk) { | ||
const styleChunk = result.chunks.find((item: ChunkDefinition) => item.name === comp.name) | ||
const styleChunk = result.chunks.find( | ||
(item: ChunkDefinition) => item.fileType === FileType.CSS | ||
) | ||
if (!styleChunk) { | ||
return | ||
} | ||
chunks.push(styleChunk) | ||
@@ -352,18 +410,41 @@ } | ||
nodesLookup[key] = compTag | ||
return compTag | ||
} | ||
const generateDynamicNode: NodeToHTML<UIDLDynamicReference, HastNode> = ( | ||
const generateDynamicNode: NodeToHTML<UIDLDynamicReference, Promise<HastNode>> = async ( | ||
node, | ||
_, | ||
nodesLookup, | ||
propDefinitions, | ||
stateDefinitions | ||
) => { | ||
stateDefinitions, | ||
subComponentOptions, | ||
structure | ||
): Promise<HastNode> => { | ||
const usedReferenceValue = getValueFromReference( | ||
node.content.id, | ||
node.content.referenceType === 'prop' ? propDefinitions : stateDefinitions | ||
) | ||
if (usedReferenceValue.type === 'element' && usedReferenceValue.defaultValue) { | ||
const slotNode = await generateElementNode( | ||
usedReferenceValue.defaultValue as UIDLElementNode, | ||
nodesLookup, | ||
propDefinitions, | ||
stateDefinitions, | ||
subComponentOptions, | ||
structure | ||
) | ||
return slotNode as HastNode | ||
} | ||
if (usedReferenceValue.type === 'element' && usedReferenceValue.defaultValue === undefined) { | ||
const spanTagWrapper = HASTBuilders.createHTMLNode('span') | ||
const commentNode = HASTBuilders.createComment(`Content for slot ${node.content.id}`) | ||
HASTUtils.addChildNode(spanTagWrapper, commentNode) | ||
return spanTagWrapper | ||
} | ||
const spanTag = HASTBuilders.createHTMLNode('span') | ||
const usedReferenceValue = | ||
node.content.referenceType === 'prop' | ||
? getValueFromReference(node.content.id, propDefinitions) | ||
: getValueFromReference(node.content.id, stateDefinitions) | ||
HASTUtils.addTextNode(spanTag, String(usedReferenceValue)) | ||
HASTUtils.addTextNode(spanTag, String(usedReferenceValue.defaultValue)) | ||
return spanTag | ||
@@ -381,6 +462,8 @@ } | ||
if (style.type === 'dynamic' && style.content?.referenceType !== 'token') { | ||
if (style.content.referenceType === 'prop') { | ||
style = getValueFromReference(style.content.id, propDefinitions) | ||
} else if (style.content.referenceType === 'state') { | ||
style = getValueFromReference(style.content.id, stateDefinitions) | ||
const referencedValue = getValueFromReference( | ||
style.content.id, | ||
style.content.referenceType === 'prop' ? propDefinitions : stateDefinitions | ||
) | ||
if (referencedValue.type === 'string' || referencedValue.type === 'number') { | ||
style = String(referencedValue.defaultValue) | ||
} | ||
@@ -393,2 +476,3 @@ node.content.style[styleKey] = typeof style === 'string' ? staticNode(style) : style | ||
const handleAttributes = ( | ||
elementType: UIDLElement['elementType'], | ||
htmlNode: HastNode, | ||
@@ -398,47 +482,104 @@ attrs: Record<string, UIDLAttributeValue>, | ||
stateDefinitions: Record<string, UIDLStateDefinition>, | ||
routeDefinitions: UIDLRouteDefinitions | ||
routeDefinitions: UIDLRouteDefinitions, | ||
outputOptions: UIDLComponentOutputOptions | ||
) => { | ||
Object.keys(attrs).forEach((attrKey) => { | ||
let attrValue = attrs[attrKey] | ||
for (const attrKey of Object.keys(attrs)) { | ||
const attrValue = attrs[attrKey] | ||
const { type, content } = attrValue | ||
if ( | ||
attrKey === 'href' && | ||
attrValue.type === 'static' && | ||
typeof attrValue.content === 'string' && | ||
attrValue.content.startsWith('/') | ||
) { | ||
attrValue = | ||
attrValue.content === '/' || | ||
attrValue.content === | ||
`/${StringUtils.camelCaseToDashCase( | ||
StringUtils.removeIllegalCharacters(routeDefinitions?.defaultValue || '') | ||
)}` | ||
? staticNode('index.html') | ||
: staticNode(`${attrValue.content.split('/').pop()}.html`) | ||
HASTUtils.addAttributeToNode(htmlNode, attrKey, String(attrValue.content)) | ||
return | ||
} | ||
switch (type) { | ||
case 'static': { | ||
if (attrKey === 'href' && typeof content === 'string' && content.startsWith('/')) { | ||
let targetLink | ||
if (attrValue.type === 'dynamic') { | ||
const value = | ||
attrValue.content.referenceType === 'prop' | ||
? getValueFromReference(attrValue.content.id, propDefinitions) | ||
: getValueFromReference(attrValue.content.id, stateDefinitions) | ||
HASTUtils.addAttributeToNode(htmlNode, attrKey, String(value)) | ||
return | ||
} | ||
const targetRoute = (routeDefinitions?.values || []).find( | ||
(route) => route.pageOptions.navLink === content | ||
) | ||
if (attrValue.type === 'raw') { | ||
HASTUtils.addAttributeToNode(htmlNode, attrKey, attrValue.content) | ||
return | ||
} | ||
if (targetRoute) { | ||
targetLink = targetRoute.pageOptions.navLink | ||
} | ||
if (typeof attrValue.content === 'boolean') { | ||
HASTUtils.addBooleanAttributeToNode(htmlNode, attrKey) | ||
return | ||
} else if (typeof attrValue.content === 'string' || typeof attrValue.content === 'number') { | ||
HASTUtils.addAttributeToNode(htmlNode, attrKey, StringUtils.encode(String(attrValue.content))) | ||
return | ||
if (!targetRoute && content === '/home') { | ||
targetLink = '/' | ||
} | ||
if (!targetLink && !targetRoute) { | ||
targetLink = content | ||
} | ||
const currentPageRoute = join(...(outputOptions?.folderPath || []), './') | ||
const localPrefix = relative( | ||
`/${currentPageRoute}`, | ||
`/${targetLink === '/' ? 'index' : targetLink}` | ||
) | ||
HASTUtils.addAttributeToNode(htmlNode, attrKey, `${localPrefix}.html`) | ||
break | ||
} | ||
if (typeof content === 'boolean') { | ||
htmlNode.properties[attrKey] = content === true ? 'true' : 'false' | ||
} else if (typeof content === 'string' || typeof attrValue.content === 'number') { | ||
let value = StringUtils.encode(String(attrValue.content)) | ||
/* | ||
elementType of image is always mapped to img. | ||
For reference, check `html-mapping` file. | ||
*/ | ||
if (elementType === 'img' && attrKey === 'src' && !isValidURL(value)) { | ||
/* | ||
By default we just prefix all the asset paths with just the | ||
assetPrefix that is configured in the project. But for `html` generators | ||
we need to prefix that with the current file location. | ||
Because, all the other frameworks have a build setup. which serves all the | ||
assets from the `public` folder. But in the case of `html` here is how it works | ||
We load a file from `index.html` the request for the image goes from | ||
'...url.../public/...image...' | ||
If it's a nested url, then the request goes from | ||
'...url/nested/public/...image..' | ||
But the nested folder is available only on the root. With this | ||
The url changes prefixes to | ||
../public/playground_assets/..image.. etc depending on the dept the file is in. | ||
*/ | ||
value = join(relative(join(...outputOptions.folderPath), './'), value) | ||
} | ||
HASTUtils.addAttributeToNode(htmlNode, attrKey, value) | ||
} | ||
break | ||
} | ||
case 'dynamic': { | ||
const value = getValueFromReference( | ||
content.id, | ||
content.referenceType === 'prop' ? propDefinitions : stateDefinitions | ||
) | ||
HASTUtils.addAttributeToNode(htmlNode, attrKey, String(value.defaultValue)) | ||
break | ||
} | ||
case 'raw': { | ||
HASTUtils.addAttributeToNode(htmlNode, attrKey, content) | ||
break | ||
} | ||
case 'element': | ||
case 'import': | ||
case 'expr': | ||
break | ||
default: { | ||
throw new HTMLComponentGeneratorError( | ||
`Received ${JSON.stringify(attrValue, null, 2)} \n in handleAttributes for html` | ||
) | ||
} | ||
} | ||
}) | ||
} | ||
} | ||
@@ -449,3 +590,3 @@ | ||
definitions: Record<string, UIDLPropDefinition> | ||
): string => { | ||
): UIDLPropDefinition | undefined => { | ||
const usedReferenceValue = definitions[key.includes('.') ? key.split('.')[0] : key] | ||
@@ -459,5 +600,5 @@ | ||
if (!usedReferenceValue.hasOwnProperty('defaultValue')) { | ||
if (['string', 'number', 'object', 'element'].includes(usedReferenceValue?.type) === false) { | ||
throw new HTMLComponentGeneratorError( | ||
`Default value is missing from dynamic reference - ${JSON.stringify( | ||
`Attribute is using dynamic value, but received of type ${JSON.stringify( | ||
usedReferenceValue, | ||
@@ -470,5 +611,8 @@ null, | ||
if (!['string', 'number', 'object'].includes(usedReferenceValue?.type)) { | ||
if ( | ||
usedReferenceValue.type !== 'element' && | ||
usedReferenceValue.hasOwnProperty('defaultValue') === false | ||
) { | ||
throw new HTMLComponentGeneratorError( | ||
`Attribute is using dynamic value, but received of type ${JSON.stringify( | ||
`Default value is missing from dynamic reference - ${JSON.stringify( | ||
usedReferenceValue, | ||
@@ -481,3 +625,3 @@ null, | ||
return String(usedReferenceValue.defaultValue) | ||
return usedReferenceValue | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
190560
1947