@teleporthq/teleport-code-generators
Advanced tools
Comparing version 0.3.0 to 0.3.1
@@ -9,3 +9,3 @@ import componentUIDL from '../../examples/uidl-samples/component-author-card.json' | ||
const { ReactComponentFlavors } = ComponentGeneratorTypes | ||
const { ReactComponentStylingFlavors } = ComponentGeneratorTypes | ||
@@ -15,3 +15,3 @@ describe('React Component Generator', () => { | ||
const generator = createReactComponentGenerator({ | ||
variation: ReactComponentFlavors.CSSModules, | ||
variation: ReactComponentStylingFlavors.CSSModules, | ||
}) | ||
@@ -30,3 +30,3 @@ | ||
const generator = createReactComponentGenerator({ | ||
variation: ReactComponentFlavors.InlineStyles, | ||
variation: ReactComponentStylingFlavors.InlineStyles, | ||
customMapping: { container: { type: 'fakediv' } }, | ||
@@ -33,0 +33,0 @@ }) |
{ | ||
"arrowParens": "always", | ||
"parser": "typescript", | ||
"printWidth": 99, | ||
"printWidth": 100, | ||
"semi": false, | ||
@@ -6,0 +6,0 @@ "singleQuote": true, |
@@ -1,11 +0,12 @@ | ||
import { ComponentPlugin, ComponentStructure } from '../../types'; | ||
import { ComponentPlugin, ComponentStructure, ChunkDefinition } from '../../types'; | ||
import { ComponentUIDL } from '../../../uidl-definitions/types'; | ||
export default class ComponentAssemblyLine { | ||
export default class AssemblyLine { | ||
private plugins; | ||
constructor(plugins?: ComponentPlugin[]); | ||
run(uidl: ComponentUIDL, initialStructure?: ComponentStructure): Promise<{ | ||
chunks: import("../../types").ChunkDefinition[]; | ||
chunks: ChunkDefinition[]; | ||
dependencies: Record<string, import("../../../uidl-definitions/types").ComponentDependency>; | ||
}>; | ||
addPlugin(plugin: ComponentPlugin): void; | ||
groupChunksByFileId(chunks: ChunkDefinition[]): Record<string, ChunkDefinition[]>; | ||
} |
@@ -38,8 +38,8 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var ComponentAssemblyLine = /** @class */ (function () { | ||
function ComponentAssemblyLine(plugins) { | ||
var AssemblyLine = /** @class */ (function () { | ||
function AssemblyLine(plugins) { | ||
if (plugins === void 0) { plugins = []; } | ||
this.plugins = plugins; | ||
} | ||
ComponentAssemblyLine.prototype.run = function (uidl, initialStructure) { | ||
AssemblyLine.prototype.run = function (uidl, initialStructure) { | ||
if (initialStructure === void 0) { initialStructure = { | ||
@@ -79,8 +79,18 @@ uidl: uidl, | ||
}; | ||
ComponentAssemblyLine.prototype.addPlugin = function (plugin) { | ||
AssemblyLine.prototype.addPlugin = function (plugin) { | ||
this.plugins.push(plugin); | ||
}; | ||
return ComponentAssemblyLine; | ||
AssemblyLine.prototype.groupChunksByFileId = function (chunks) { | ||
return chunks.reduce(function (chunksByFileId, chunk) { | ||
var fileId = (chunk.meta && chunk.meta.fileId) || 'default'; | ||
if (!chunksByFileId[fileId]) { | ||
chunksByFileId[fileId] = []; | ||
} | ||
chunksByFileId[fileId].push(chunk); | ||
return chunksByFileId; | ||
}, {}); | ||
}; | ||
return AssemblyLine; | ||
}()); | ||
exports.default = ComponentAssemblyLine; | ||
exports.default = AssemblyLine; | ||
//# sourceMappingURL=index.js.map |
@@ -5,15 +5,2 @@ "use strict"; | ||
var html_to_string_1 = require("./generators/html-to-string"); | ||
var removeItemsInArray = function (arrayToRemoveFrom, itemsToRemove) { | ||
return arrayToRemoveFrom.filter(function (item) { | ||
return itemsToRemove.indexOf(item) === -1; | ||
}); | ||
}; | ||
var removeChildDependency = function (children, targetChunkName) { | ||
return children.reduce(function (acc, child) { | ||
if (child.chunkName !== targetChunkName) { | ||
acc.push(child); | ||
} | ||
return acc; | ||
}, []); | ||
}; | ||
var Builder = /** @class */ (function () { | ||
@@ -212,2 +199,15 @@ function Builder(chunkDefinitions) { | ||
exports.default = Builder; | ||
var removeItemsInArray = function (arrayToRemoveFrom, itemsToRemove) { | ||
return arrayToRemoveFrom.filter(function (item) { | ||
return itemsToRemove.indexOf(item) === -1; | ||
}); | ||
}; | ||
var removeChildDependency = function (children, targetChunkName) { | ||
return children.reduce(function (acc, child) { | ||
if (child.chunkName !== targetChunkName) { | ||
acc.push(child); | ||
} | ||
return acc; | ||
}, []); | ||
}; | ||
//# sourceMappingURL=index.js.map |
@@ -1,3 +0,3 @@ | ||
export { default as ComponentAssemblyLine } from './assembly-line'; | ||
export { default as AssemblyLine } from './assembly-line'; | ||
export { default as Builder } from './builder'; | ||
export { default as Resolver } from './resolver'; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var assembly_line_1 = require("./assembly-line"); | ||
exports.ComponentAssemblyLine = assembly_line_1.default; | ||
exports.AssemblyLine = assembly_line_1.default; | ||
var builder_1 = require("./builder"); | ||
@@ -6,0 +6,0 @@ exports.Builder = builder_1.default; |
@@ -14,4 +14,81 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var uidl_utils_1 = require("../../utils/uidl-utils"); | ||
var constants_1 = require("../../constants"); | ||
var STYLE_PROPERTIES_WITH_URL = ['background', 'backgroundImage']; | ||
var ATTRIBUTES_WITH_URL = ['url', 'srcset']; | ||
exports.resolveContentNode = function (node, elementsMapping, localDependenciesPrefix, assetsPrefix) { | ||
var mappedElement = elementsMapping[node.type] || { type: node.type }; | ||
node.type = mappedElement.type; | ||
// If the mapping contains children, insert that structure into the UIDL | ||
if (mappedElement.children) { | ||
var originalNodeChildren = node.children || []; | ||
var originalAttrs = node.attrs || {}; | ||
var replacingNode = __assign({}, node, { children: JSON.parse(JSON.stringify(mappedElement.children)) }); | ||
insertChildrenIntoNode(replacingNode, originalNodeChildren, originalAttrs); | ||
node.children = replacingNode.children; | ||
} | ||
// Resolve dependency with the UIDL having priority | ||
if (node.dependency || mappedElement.dependency) { | ||
node.dependency = resolveDependency(mappedElement, node.dependency, localDependenciesPrefix); | ||
} | ||
// Resolve assets prefix inside style (ex: background-image) | ||
if (node.styles && assetsPrefix) { | ||
node.styles = prefixAssetURLs(node.styles, assetsPrefix); | ||
} | ||
// Prefix the attributes which may point to local assets | ||
if (node.attrs && assetsPrefix) { | ||
ATTRIBUTES_WITH_URL.forEach(function (attribute) { | ||
if (node.attrs[attribute]) { | ||
node.attrs[attribute] = uidl_utils_1.prefixPlaygroundAssetsURL(assetsPrefix, node.attrs[attribute]); | ||
} | ||
}); | ||
} | ||
// Merge UIDL attributes to the attributes coming from the mapping object | ||
if (mappedElement.attrs) { | ||
node.attrs = mergeAttributes(mappedElement, node.attrs); | ||
} | ||
// The UIDL has priority over the mapping repeat | ||
var repeatStructure = node.repeat || mappedElement.repeat; | ||
if (repeatStructure) { | ||
var dataSource = repeatStructure.dataSource, content = repeatStructure.content; | ||
// Data source might be preset on a referenced attribute in the uidl node | ||
// ex: attrs[options] in case of a dropdown primitive with select/options | ||
if (typeof dataSource === 'string' && dataSource.startsWith('$attrs.') && node.attrs) { | ||
var nodeDataSourceAttr = dataSource.replace('$attrs.', ''); | ||
repeatStructure.dataSource = node.attrs[nodeDataSourceAttr]; | ||
} | ||
// The content inside the repeat must also be mapped like any regular content node | ||
repeatStructure.content = exports.resolveContentNode(content, elementsMapping, localDependenciesPrefix, assetsPrefix); | ||
node.repeat = repeatStructure; | ||
} | ||
// If the node has multiple state branches, each content needs to be resolved | ||
if (node.type === 'state' && node.states) { | ||
node.states = node.states.map(function (stateBranch) { | ||
if (typeof stateBranch.content === 'string') { | ||
return stateBranch; | ||
} | ||
else { | ||
return __assign({}, stateBranch, { content: exports.resolveContentNode(stateBranch.content, elementsMapping, localDependenciesPrefix, assetsPrefix) }); | ||
} | ||
}); | ||
} | ||
// Traverse the UIDL | ||
if (node.children) { | ||
node.children = node.children.map(function (child) { | ||
if (typeof child === 'string') { | ||
return child; | ||
} | ||
else { | ||
return exports.resolveContentNode(child, elementsMapping, localDependenciesPrefix, assetsPrefix); | ||
} | ||
}); | ||
} | ||
return node; | ||
}; | ||
/** | ||
* Prefixes all urls inside the style object with the assetsPrefix | ||
* @param styles the style object on the current node | ||
* @param assetsPrefix a string representing the asset prefix | ||
*/ | ||
var prefixAssetURLs = function (styles, assetsPrefix) { | ||
var whitelistStyleProperties = ['background', 'backgroundImage']; | ||
// iterate through all the style keys | ||
@@ -31,8 +108,8 @@ return Object.keys(styles).reduce(function (acc, styleKey) { | ||
// only whitelisted style properties are checked | ||
if (whitelistStyleProperties.includes(styleKey) && styleValue.includes('url("')) { | ||
// position index after the double quotes to introduce the url prefix before | ||
var startIndex = styleValue.indexOf('url("') + 5; | ||
if (STYLE_PROPERTIES_WITH_URL.includes(styleKey) && styleValue.includes(constants_1.ASSETS_IDENTIFIER)) { | ||
// split the string at the beginning of the ASSETS_IDENTIFIER string | ||
var startIndex = styleValue.indexOf(constants_1.ASSETS_IDENTIFIER); | ||
acc[styleKey] = | ||
styleValue.slice(0, startIndex) + | ||
prefixRelativeURL(assetsPrefix, styleValue.slice(startIndex, styleValue.length - 1)); | ||
uidl_utils_1.prefixPlaygroundAssetsURL(assetsPrefix, styleValue.slice(startIndex, styleValue.length)); | ||
} | ||
@@ -45,17 +122,3 @@ else { | ||
}; | ||
/** | ||
* Concatenates the prefix to the URL string. | ||
* If the url starts with 'http', the return value will be the 'originalString' | ||
* If the url does not start with a '/' it also appends that | ||
*/ | ||
var prefixRelativeURL = function (prefix, originalString) { | ||
if (originalString.startsWith('http')) { | ||
return originalString; | ||
} | ||
if (originalString.startsWith('/')) { | ||
return prefix + originalString; | ||
} | ||
return prefix + "/" + originalString; | ||
}; | ||
var mergeAttributes = function (mappedElement, uidlAttrs, assetsPrefix) { | ||
var mergeAttributes = function (mappedElement, uidlAttrs) { | ||
// We gather the results here uniting the mapped attributes and the uidl attributes. | ||
@@ -79,6 +142,3 @@ var resolvedAttrs = {}; | ||
if (uidlAttrs && uidlAttrs[uidlAttributeKey]) { | ||
resolvedAttrs[key] = | ||
key === 'src' && assetsPrefix | ||
? prefixRelativeURL(assetsPrefix, uidlAttrs[uidlAttributeKey]) | ||
: uidlAttrs[uidlAttributeKey]; | ||
resolvedAttrs[key] = uidlAttrs[uidlAttributeKey]; | ||
mappedAttributes.push(uidlAttributeKey); | ||
@@ -114,3 +174,16 @@ } | ||
// Traverses the mapped elements children and inserts the original children of the node being mapped. | ||
var insertChildrenIntoNode = function (node, originalChildren) { | ||
var insertChildrenIntoNode = function (node, originalChildren, originalAttrs) { | ||
// The same kind of referencing that is done in the mergeAttributes function | ||
// TODO: Extract duplicate code and apply in both instances (merge attributes and solving children nodes) | ||
// Explained here: https://github.com/teleporthq/teleport-code-generators/issues/44 | ||
// Object.keys(node.attrs).forEach((attrKey) => { | ||
// if (typeof node.attrs[attrKey] === 'string' && node.attrs[attrKey].startsWith('$attrs.')) { | ||
// const referencedAttributeKey = node.attrs[attrKey].replace('$attrs.', '') | ||
// if (originalAttrs[referencedAttributeKey]) { | ||
// node.attrs[attrKey] = originalAttrs[referencedAttributeKey] | ||
// // since the attribute is mapped in the children, we assume it is not longer needed on the root node | ||
// delete originalAttrs[referencedAttributeKey] | ||
// } | ||
// } | ||
// }) | ||
if (!node.children) { | ||
@@ -132,3 +205,3 @@ return; | ||
// The child node is pushed after the $children token was replaced | ||
insertChildrenIntoNode(child, originalChildren); | ||
insertChildrenIntoNode(child, originalChildren, originalAttrs); | ||
acc.push(child); | ||
@@ -138,62 +211,2 @@ return acc; | ||
}; | ||
exports.resolveContentNode = function (node, elementsMapping, localDependenciesPrefix, assetsPrefix) { | ||
var mappedElement = elementsMapping[node.type] || { type: node.type }; | ||
node.type = mappedElement.type; | ||
// Resolve dependency with the UIDL having priority | ||
if (node.dependency || mappedElement.dependency) { | ||
node.dependency = resolveDependency(mappedElement, node.dependency, localDependenciesPrefix); | ||
} | ||
// Resolve assets prefix inside style (ex: background-image) | ||
if (node.styles && assetsPrefix) { | ||
node.styles = prefixAssetURLs(node.styles, assetsPrefix); | ||
} | ||
// Merge UIDL attributes to the attributes coming from the mapping object | ||
if (mappedElement.attrs) { | ||
node.attrs = mergeAttributes(mappedElement, node.attrs, assetsPrefix); | ||
} | ||
// If the mapping contains children, insert that structure into the UIDL | ||
if (mappedElement.children) { | ||
var originalNodeChildren = node.children || []; | ||
var replacingNode = __assign({}, node, { children: JSON.parse(JSON.stringify(mappedElement.children)) }); | ||
insertChildrenIntoNode(replacingNode, originalNodeChildren); | ||
node.children = replacingNode.children; | ||
} | ||
// The UIDL has priority over the mapping repeat | ||
var repeatStructure = node.repeat || mappedElement.repeat; | ||
if (repeatStructure) { | ||
var dataSource = repeatStructure.dataSource, content = repeatStructure.content; | ||
// Data source might be preset on a referenced attribute in the uidl node | ||
// ex: attrs[options] in case of a dropdown primitive with select/options | ||
if (typeof dataSource === 'string' && dataSource.startsWith('$attrs.') && node.attrs) { | ||
var nodeDataSourceAttr = dataSource.replace('$attrs.', ''); | ||
repeatStructure.dataSource = node.attrs[nodeDataSourceAttr]; | ||
} | ||
// The content inside the repeat must also be mapped like any regular content node | ||
repeatStructure.content = exports.resolveContentNode(content, elementsMapping, localDependenciesPrefix, assetsPrefix); | ||
node.repeat = repeatStructure; | ||
} | ||
// If the node has multiple state branches, each content needs to be resolved | ||
if (node.type === 'state' && node.states) { | ||
node.states = node.states.map(function (stateBranch) { | ||
if (typeof stateBranch.content === 'string') { | ||
return stateBranch; | ||
} | ||
else { | ||
return __assign({}, stateBranch, { content: exports.resolveContentNode(stateBranch.content, elementsMapping, localDependenciesPrefix, assetsPrefix) }); | ||
} | ||
}); | ||
} | ||
// Traverse the UIDL | ||
if (node.children) { | ||
node.children = node.children.map(function (child) { | ||
if (typeof child === 'string') { | ||
return child; | ||
} | ||
else { | ||
return exports.resolveContentNode(child, elementsMapping, localDependenciesPrefix, assetsPrefix); | ||
} | ||
}); | ||
} | ||
return node; | ||
}; | ||
//# sourceMappingURL=utils.js.map |
@@ -40,2 +40,20 @@ "use strict"; | ||
var js_ast_1 = require("../../utils/js-ast"); | ||
exports.createPlugin = function (config) { | ||
var _a = config || {}, _b = _a.importLibsChunkName, importLibsChunkName = _b === void 0 ? 'import-libs' : _b, _c = _a.importPackagesChunkName, importPackagesChunkName = _c === void 0 ? 'import-packages' : _c, _d = _a.importLocalsChunkName, importLocalsChunkName = _d === void 0 ? 'import-local' : _d, _e = _a.fileId, fileId = _e === void 0 ? null : _e; | ||
var importPlugin = function (structure) { return __awaiter(_this, void 0, void 0, function () { | ||
var dependencies, libraryDependencies, packageDependencies, localDependencies; | ||
return __generator(this, function (_a) { | ||
dependencies = structure.dependencies; | ||
libraryDependencies = groupDependenciesByPackage(dependencies, 'library'); | ||
packageDependencies = groupDependenciesByPackage(dependencies, 'package'); | ||
localDependencies = groupDependenciesByPackage(dependencies, 'local'); | ||
addImportChunk(structure.chunks, libraryDependencies, importLibsChunkName, fileId); | ||
addImportChunk(structure.chunks, packageDependencies, importPackagesChunkName, fileId); | ||
addImportChunk(structure.chunks, localDependencies, importLocalsChunkName, fileId); | ||
return [2 /*return*/, structure]; | ||
}); | ||
}); }; | ||
return importPlugin; | ||
}; | ||
exports.default = exports.createPlugin(); | ||
var groupDependenciesByPackage = function (dependencies, packageType) { | ||
@@ -77,20 +95,2 @@ var result = {}; | ||
}; | ||
exports.createPlugin = function (config) { | ||
var _a = config || {}, _b = _a.importLibsChunkName, importLibsChunkName = _b === void 0 ? 'import-libs' : _b, _c = _a.importPackagesChunkName, importPackagesChunkName = _c === void 0 ? 'import-packages' : _c, _d = _a.importLocalsChunkName, importLocalsChunkName = _d === void 0 ? 'import-local' : _d, _e = _a.fileId, fileId = _e === void 0 ? null : _e; | ||
var importPlugin = function (structure) { return __awaiter(_this, void 0, void 0, function () { | ||
var dependencies, libraryDependencies, packageDependencies, localDependencies; | ||
return __generator(this, function (_a) { | ||
dependencies = structure.dependencies; | ||
libraryDependencies = groupDependenciesByPackage(dependencies, 'library'); | ||
packageDependencies = groupDependenciesByPackage(dependencies, 'package'); | ||
localDependencies = groupDependenciesByPackage(dependencies, 'local'); | ||
addImportChunk(structure.chunks, libraryDependencies, importLibsChunkName, fileId); | ||
addImportChunk(structure.chunks, packageDependencies, importPackagesChunkName, fileId); | ||
addImportChunk(structure.chunks, localDependencies, importLocalsChunkName, fileId); | ||
return [2 /*return*/, structure]; | ||
}); | ||
}); }; | ||
return importPlugin; | ||
}; | ||
exports.default = exports.createPlugin(); | ||
//# sourceMappingURL=import-statements.js.map |
@@ -15,3 +15,3 @@ "use strict"; | ||
path: 'react', | ||
version: '16.6.3', | ||
version: '16.8.3', | ||
}; | ||
@@ -21,3 +21,3 @@ dependencies.ReactDOM = { | ||
path: 'react-dom', | ||
version: '16.6.3', | ||
version: '16.8.3', | ||
}; | ||
@@ -24,0 +24,0 @@ dependencies.Router = { |
@@ -1,5 +0,2 @@ | ||
import * as t from '@babel/types'; | ||
import { ComponentPlugin, ComponentPluginFactory, StateIdentifier } from '../../../types'; | ||
import { ContentNode, PropDefinition, ComponentDependency } from '../../../../uidl-definitions/types'; | ||
export declare const generateTreeStructure: (content: ContentNode, propDefinitions: Record<string, PropDefinition>, stateIdentifiers: Record<string, StateIdentifier>, nodesLookup: Record<string, t.JSXElement>, dependencies: Record<string, ComponentDependency>) => t.JSXElement; | ||
import { ComponentPlugin, ComponentPluginFactory } from '../../../types'; | ||
interface JSXConfig { | ||
@@ -6,0 +3,0 @@ componentChunkName: string; |
@@ -54,103 +54,2 @@ "use strict"; | ||
var helpers_1 = require("../../../utils/helpers"); | ||
/** | ||
* | ||
* @param tag the ref to the AST tag under construction | ||
* @param key the key of the attribute that should be added on the current AST node | ||
* @param value the value(string, number, bool) of the attribute that should be added on the current AST node | ||
*/ | ||
var addAttributeToTag = function (tag, key, value) { | ||
if (typeof value !== 'string') { | ||
jsx_ast_1.addAttributeToJSXTag(tag, { name: key, value: value }); | ||
return; | ||
} | ||
if (value.startsWith('$props.')) { | ||
var dynamicPropValue = value.replace('$props.', ''); | ||
jsx_ast_1.addDynamicPropOnJsxOpeningTag(tag, key, dynamicPropValue, 'props'); | ||
} | ||
else if (value.startsWith('$state.')) { | ||
var dynamicPropValue = value.replace('$state.', ''); | ||
jsx_ast_1.addDynamicPropOnJsxOpeningTag(tag, key, dynamicPropValue); | ||
} | ||
else if (value === '$item' || value === '$index') { | ||
jsx_ast_1.addDynamicPropOnJsxOpeningTag(tag, key, value.slice(1)); | ||
} | ||
else { | ||
jsx_ast_1.addAttributeToJSXTag(tag, { name: key, value: value }); | ||
} | ||
}; | ||
var addTextElementToTag = function (tag, text) { | ||
if (text.startsWith('$props.') && !text.endsWith('$props.')) { | ||
jsx_ast_1.addDynamicChild(tag, text.replace('$props.', ''), 'props'); | ||
} | ||
else if (text.startsWith('$state.') && !text.endsWith('$state.')) { | ||
jsx_ast_1.addDynamicChild(tag, text.replace('$state.', '')); | ||
} | ||
else if (text === '$item' || text === '$index') { | ||
jsx_ast_1.addDynamicChild(tag, text.slice(1)); | ||
} | ||
else { | ||
jsx_ast_1.addChildJSXText(tag, text); | ||
} | ||
}; | ||
exports.generateTreeStructure = function (content, propDefinitions, stateIdentifiers, nodesLookup, dependencies) { | ||
var type = content.type, children = content.children, key = content.key, attrs = content.attrs, dependency = content.dependency, events = content.events, repeat = content.repeat; | ||
var mainTag = jsx_ast_1.generateASTDefinitionForJSXTag(type); | ||
if (attrs) { | ||
Object.keys(attrs).forEach(function (attrKey) { | ||
addAttributeToTag(mainTag, attrKey, attrs[attrKey]); | ||
}); | ||
} | ||
if (dependency) { | ||
// Make a copy to avoid reference leaking | ||
dependencies[type] = __assign({}, dependency); | ||
} | ||
if (events) { | ||
Object.keys(events).forEach(function (eventKey) { | ||
utils_1.addEventHandlerToTag(mainTag, eventKey, events[eventKey], stateIdentifiers, propDefinitions); | ||
}); | ||
} | ||
if (repeat) { | ||
var repeatContent = repeat.content, dataSource = repeat.dataSource, meta = repeat.meta; | ||
var contentAST = exports.generateTreeStructure(repeatContent, propDefinitions, stateIdentifiers, nodesLookup, dependencies); | ||
addAttributeToTag(contentAST, 'key', '$item'); | ||
var repeatAST = utils_1.makeRepeatStructureWithMap(dataSource, contentAST, meta); | ||
mainTag.children.push(repeatAST); | ||
} | ||
if (children) { | ||
children.forEach(function (child) { | ||
if (!child) { | ||
return; | ||
} | ||
if (typeof child === 'string') { | ||
addTextElementToTag(mainTag, child); | ||
return; | ||
} | ||
if (child.type === 'state') { | ||
var _a = child.states, states = _a === void 0 ? [] : _a, stateKey_1 = child.key; | ||
states.forEach(function (stateBranch) { | ||
var stateContent = stateBranch.content; | ||
var stateIdentifier = stateIdentifiers[stateKey_1]; | ||
if (!stateIdentifier) { | ||
return; | ||
} | ||
if (typeof stateContent === 'string') { | ||
var jsxExpression = jsx_ast_1.createConditionalJSXExpression(stateContent, stateBranch.value, stateIdentifier); | ||
mainTag.children.push(jsxExpression); | ||
} | ||
else { | ||
var stateChildSubTree = exports.generateTreeStructure(stateContent, propDefinitions, stateIdentifiers, nodesLookup, dependencies); | ||
var jsxExpression = jsx_ast_1.createConditionalJSXExpression(stateChildSubTree, stateBranch.value, stateIdentifier); | ||
mainTag.children.push(jsxExpression); | ||
} | ||
}); | ||
return; | ||
} | ||
var childTag = exports.generateTreeStructure(child, propDefinitions, stateIdentifiers, nodesLookup, dependencies); | ||
jsx_ast_1.addChildJSXTag(mainTag, childTag); | ||
}); | ||
} | ||
// UIDL name should be unique | ||
nodesLookup[key] = mainTag; | ||
return mainTag; | ||
}; | ||
exports.createPlugin = function (config) { | ||
@@ -165,2 +64,3 @@ var _a = config || {}, _b = _a.componentChunkName, componentChunkName = _b === void 0 ? 'react-component' : _b, _c = _a.exportChunkName, exportChunkName = _c === void 0 ? 'export' : _c, _d = _a.importChunkName, importChunkName = _d === void 0 ? 'import' : _d; | ||
path: 'react', | ||
version: '16.8.3', | ||
}; | ||
@@ -172,2 +72,3 @@ stateIdentifiers = {}; | ||
path: 'react', | ||
version: '16.8.3', | ||
meta: { | ||
@@ -189,3 +90,3 @@ namedImport: true, | ||
nodesLookup = {}; | ||
jsxTagStructure = exports.generateTreeStructure(uidl.content, uidl.propDefinitions || {}, stateIdentifiers, nodesLookup, dependencies); | ||
jsxTagStructure = generateTreeStructure(uidl.content, uidl.propDefinitions || {}, stateIdentifiers, nodesLookup, dependencies); | ||
pureComponent = utils_1.makePureComponent(uidl.name, stateIdentifiers, jsxTagStructure); | ||
@@ -217,2 +118,63 @@ structure.chunks.push({ | ||
exports.default = exports.createPlugin(); | ||
var generateTreeStructure = function (content, propDefinitions, stateIdentifiers, nodesLookup, dependencies) { | ||
var type = content.type, children = content.children, key = content.key, attrs = content.attrs, dependency = content.dependency, events = content.events, repeat = content.repeat; | ||
var mainTag = jsx_ast_1.generateASTDefinitionForJSXTag(type); | ||
if (attrs) { | ||
Object.keys(attrs).forEach(function (attrKey) { | ||
utils_1.addAttributeToTag(mainTag, attrKey, attrs[attrKey]); | ||
}); | ||
} | ||
if (dependency) { | ||
// Make a copy to avoid reference leaking | ||
dependencies[type] = __assign({}, dependency); | ||
} | ||
if (events) { | ||
Object.keys(events).forEach(function (eventKey) { | ||
utils_1.addEventHandlerToTag(mainTag, eventKey, events[eventKey], stateIdentifiers, propDefinitions); | ||
}); | ||
} | ||
if (repeat) { | ||
var repeatContent = repeat.content, dataSource = repeat.dataSource, meta = repeat.meta; | ||
var contentAST = generateTreeStructure(repeatContent, propDefinitions, stateIdentifiers, nodesLookup, dependencies); | ||
utils_1.addAttributeToTag(contentAST, 'key', '$item'); | ||
var repeatAST = utils_1.makeRepeatStructureWithMap(dataSource, contentAST, meta); | ||
mainTag.children.push(repeatAST); | ||
} | ||
if (children) { | ||
children.forEach(function (child) { | ||
if (!child) { | ||
return; | ||
} | ||
if (typeof child === 'string') { | ||
utils_1.addTextElementToTag(mainTag, child); | ||
return; | ||
} | ||
if (child.type === 'state') { | ||
var _a = child.states, states = _a === void 0 ? [] : _a, stateKey_1 = child.key; | ||
states.forEach(function (stateBranch) { | ||
var stateContent = stateBranch.content; | ||
var stateIdentifier = stateIdentifiers[stateKey_1]; | ||
if (!stateIdentifier) { | ||
return; | ||
} | ||
if (typeof stateContent === 'string') { | ||
var jsxExpression = jsx_ast_1.createConditionalJSXExpression(stateContent, stateBranch.value, stateIdentifier); | ||
mainTag.children.push(jsxExpression); | ||
} | ||
else { | ||
var stateChildSubTree = generateTreeStructure(stateContent, propDefinitions, stateIdentifiers, nodesLookup, dependencies); | ||
var jsxExpression = jsx_ast_1.createConditionalJSXExpression(stateChildSubTree, stateBranch.value, stateIdentifier); | ||
mainTag.children.push(jsxExpression); | ||
} | ||
}); | ||
return; | ||
} | ||
var childTag = generateTreeStructure(child, propDefinitions, stateIdentifiers, nodesLookup, dependencies); | ||
jsx_ast_1.addChildJSXTag(mainTag, childTag); | ||
}); | ||
} | ||
// UIDL name should be unique | ||
nodesLookup[key] = mainTag; | ||
return mainTag; | ||
}; | ||
//# sourceMappingURL=index.js.map |
@@ -11,1 +11,9 @@ import * as types from '@babel/types'; | ||
export declare const makeRepeatStructureWithMap: (dataSource: string | any[], content: types.JSXElement, meta?: any, t?: typeof types) => types.JSXExpressionContainer; | ||
/** | ||
* | ||
* @param tag the ref to the AST tag under construction | ||
* @param key the key of the attribute that should be added on the current AST node | ||
* @param value the value(string, number, bool) of the attribute that should be added on the current AST node | ||
*/ | ||
export declare const addAttributeToTag: (tag: types.JSXElement, key: string, value: any) => void; | ||
export declare const addTextElementToTag: (tag: types.JSXElement, text: string) => void; |
@@ -12,33 +12,3 @@ "use strict"; | ||
var js_ast_1 = require("../../../utils/js-ast"); | ||
var createStateChangeStatement = function (eventHandlerStatement, stateIdentifiers, t) { | ||
if (t === void 0) { t = types; } | ||
if (!eventHandlerStatement.modifies) { | ||
console.warn("No state identifier referenced under the \"modifies\" field"); | ||
return null; | ||
} | ||
var stateKey = eventHandlerStatement.modifies; | ||
var stateIdentifier = stateIdentifiers[stateKey]; | ||
if (!stateIdentifier) { | ||
console.warn("No state hook was found for \"" + stateKey + "\""); | ||
return null; | ||
} | ||
var stateSetterArgument = eventHandlerStatement.newState === '$toggle' | ||
? t.unaryExpression('!', t.identifier(stateIdentifier.key)) | ||
: js_ast_1.convertValueToLiteral(eventHandlerStatement.newState, stateIdentifier.type); | ||
return t.expressionStatement(t.callExpression(t.identifier(stateIdentifier.setter), [stateSetterArgument])); | ||
}; | ||
var createPropCallStatement = function (eventHandlerStatement, propDefinitions, t) { | ||
if (t === void 0) { t = types; } | ||
var propFunctionKey = eventHandlerStatement.calls, _a = eventHandlerStatement.args, args = _a === void 0 ? [] : _a; | ||
if (!propFunctionKey) { | ||
console.warn("No prop definition referenced under the \"calls\" field"); | ||
return null; | ||
} | ||
var propDefinition = propDefinitions[propFunctionKey]; | ||
if (!propDefinition || propDefinition.type !== 'func') { | ||
console.warn("No prop definition was found for \"" + propFunctionKey + "\""); | ||
return null; | ||
} | ||
return t.expressionStatement(t.callExpression(t.identifier('props.' + propFunctionKey), args.map(function (arg) { return js_ast_1.convertValueToLiteral(arg); }).slice())); | ||
}; | ||
var jsx_ast_1 = require("../../../utils/jsx-ast"); | ||
// Adds all the event handlers and all the instructions for each event handler | ||
@@ -77,2 +47,33 @@ // in case there is more than one specified in the UIDL | ||
}; | ||
var createPropCallStatement = function (eventHandlerStatement, propDefinitions, t) { | ||
if (t === void 0) { t = types; } | ||
var propFunctionKey = eventHandlerStatement.calls, _a = eventHandlerStatement.args, args = _a === void 0 ? [] : _a; | ||
if (!propFunctionKey) { | ||
console.warn("No prop definition referenced under the \"calls\" field"); | ||
return null; | ||
} | ||
var propDefinition = propDefinitions[propFunctionKey]; | ||
if (!propDefinition || propDefinition.type !== 'func') { | ||
console.warn("No prop definition was found for \"" + propFunctionKey + "\""); | ||
return null; | ||
} | ||
return t.expressionStatement(t.callExpression(t.identifier('props.' + propFunctionKey), args.map(function (arg) { return js_ast_1.convertValueToLiteral(arg); }).slice())); | ||
}; | ||
var createStateChangeStatement = function (eventHandlerStatement, stateIdentifiers, t) { | ||
if (t === void 0) { t = types; } | ||
if (!eventHandlerStatement.modifies) { | ||
console.warn("No state identifier referenced under the \"modifies\" field"); | ||
return null; | ||
} | ||
var stateKey = eventHandlerStatement.modifies; | ||
var stateIdentifier = stateIdentifiers[stateKey]; | ||
if (!stateIdentifier) { | ||
console.warn("No state hook was found for \"" + stateKey + "\""); | ||
return null; | ||
} | ||
var stateSetterArgument = eventHandlerStatement.newState === '$toggle' | ||
? t.unaryExpression('!', t.identifier(stateIdentifier.key)) | ||
: js_ast_1.convertValueToLiteral(eventHandlerStatement.newState, stateIdentifier.type); | ||
return t.expressionStatement(t.callExpression(t.identifier(stateIdentifier.setter), [stateSetterArgument])); | ||
}; | ||
exports.makePureComponent = function (name, stateIdentifiers, jsxTagTree, t) { | ||
@@ -113,2 +114,42 @@ if (t === void 0) { t = types; } | ||
}; | ||
/** | ||
* | ||
* @param tag the ref to the AST tag under construction | ||
* @param key the key of the attribute that should be added on the current AST node | ||
* @param value the value(string, number, bool) of the attribute that should be added on the current AST node | ||
*/ | ||
exports.addAttributeToTag = function (tag, key, value) { | ||
if (typeof value !== 'string') { | ||
jsx_ast_1.addAttributeToJSXTag(tag, { name: key, value: value }); | ||
return; | ||
} | ||
if (value.startsWith('$props.')) { | ||
var dynamicPropValue = value.replace('$props.', ''); | ||
jsx_ast_1.addDynamicPropOnJsxOpeningTag(tag, key, dynamicPropValue, 'props'); | ||
} | ||
else if (value.startsWith('$state.')) { | ||
var dynamicPropValue = value.replace('$state.', ''); | ||
jsx_ast_1.addDynamicPropOnJsxOpeningTag(tag, key, dynamicPropValue); | ||
} | ||
else if (value === '$item' || value === '$index') { | ||
jsx_ast_1.addDynamicPropOnJsxOpeningTag(tag, key, value.slice(1)); | ||
} | ||
else { | ||
jsx_ast_1.addAttributeToJSXTag(tag, { name: key, value: value }); | ||
} | ||
}; | ||
exports.addTextElementToTag = function (tag, text) { | ||
if (text.startsWith('$props.') && !text.endsWith('$props.')) { | ||
jsx_ast_1.addDynamicChild(tag, text.replace('$props.', ''), 'props'); | ||
} | ||
else if (text.startsWith('$state.') && !text.endsWith('$state.')) { | ||
jsx_ast_1.addDynamicChild(tag, text.replace('$state.', '')); | ||
} | ||
else if (text === '$item' || text === '$index') { | ||
jsx_ast_1.addDynamicChild(tag, text.slice(1)); | ||
} | ||
else { | ||
jsx_ast_1.addChildJSXText(tag, text); | ||
} | ||
}; | ||
//# sourceMappingURL=utils.js.map |
@@ -49,2 +49,25 @@ "use strict"; | ||
var js_ast_1 = require("../../utils/js-ast"); | ||
exports.createPlugin = function (config) { | ||
var _a = (config || {}).componentChunkName, componentChunkName = _a === void 0 ? 'react-component' : _a; | ||
/** | ||
* Generate the inlines stlye definition as a AST block which will represent the | ||
* defined styles of this component in UIDL | ||
* | ||
* @param structure : ComponentStructure | ||
*/ | ||
var reactInlineStyleComponentPlugin = function (structure) { return __awaiter(_this, void 0, void 0, function () { | ||
var uidl, chunks, componentChunk; | ||
return __generator(this, function (_a) { | ||
uidl = structure.uidl, chunks = structure.chunks; | ||
componentChunk = chunks.find(function (chunk) { return chunk.name === componentChunkName; }); | ||
if (!componentChunk) { | ||
return [2 /*return*/, structure]; | ||
} | ||
enhanceJSXWithStyles(uidl.content, componentChunk.meta.nodesLookup); | ||
return [2 /*return*/, structure]; | ||
}); | ||
}); }; | ||
return reactInlineStyleComponentPlugin; | ||
}; | ||
exports.default = exports.createPlugin(); | ||
var prepareDynamicProps = function (styles) { | ||
@@ -91,25 +114,2 @@ return Object.keys(styles).reduce(function (acc, key) { | ||
}; | ||
exports.createPlugin = function (config) { | ||
var _a = (config || {}).componentChunkName, componentChunkName = _a === void 0 ? 'react-component' : _a; | ||
/** | ||
* Generate the inlines stlye definition as a AST block which will represent the | ||
* defined styles of this component in UIDL | ||
* | ||
* @param structure : ComponentStructure | ||
*/ | ||
var reactInlineStyleComponentPlugin = function (structure) { return __awaiter(_this, void 0, void 0, function () { | ||
var uidl, chunks, componentChunk; | ||
return __generator(this, function (_a) { | ||
uidl = structure.uidl, chunks = structure.chunks; | ||
componentChunk = chunks.find(function (chunk) { return chunk.name === componentChunkName; }); | ||
if (!componentChunk) { | ||
return [2 /*return*/, structure]; | ||
} | ||
enhanceJSXWithStyles(uidl.content, componentChunk.meta.nodesLookup); | ||
return [2 /*return*/, structure]; | ||
}); | ||
}); }; | ||
return reactInlineStyleComponentPlugin; | ||
}; | ||
exports.default = exports.createPlugin(); | ||
//# sourceMappingURL=react-inline-styles.js.map |
@@ -61,40 +61,2 @@ "use strict"; | ||
var helpers_1 = require("../../utils/helpers"); | ||
var prepareDynamicProps = function (styles) { | ||
return Object.keys(styles).reduce(function (acc, key) { | ||
var value = styles[key]; | ||
if (typeof value === 'string' && value.startsWith('$props.')) { | ||
acc[key] = new js_ast_1.ParsedASTNode(t.arrowFunctionExpression([t.identifier('props')], t.memberExpression(t.identifier('props'), t.identifier(value.replace('$props.', ''))))); | ||
} | ||
else { | ||
acc[key] = styles[key]; | ||
} | ||
return acc; | ||
}, {}); | ||
}; | ||
var generateStyleTagStrings = function (content, nodesLookup) { | ||
var accumulator = {}; | ||
var styles = content.styles, children = content.children, key = content.key, repeat = content.repeat; | ||
if (styles) { | ||
var root = nodesLookup[key]; | ||
var className = helpers_1.cammelCaseToDashCase(key); | ||
accumulator[className] = prepareDynamicProps(styles); | ||
// addClassStringOnJSXTag(root.node, className) | ||
jsx_ast_1.addDynamicPropOnJsxOpeningTag(root, 'className', "classes['" + className + "']", 'props'); | ||
} | ||
if (repeat) { | ||
var items = generateStyleTagStrings(repeat.content, nodesLookup); | ||
accumulator = __assign({}, accumulator, items); | ||
} | ||
if (children) { | ||
children.forEach(function (child) { | ||
if (typeof child === 'string') { | ||
return; | ||
} | ||
// only call on children if they are not strings | ||
var items = generateStyleTagStrings(child, nodesLookup); | ||
accumulator = __assign({}, accumulator, items); | ||
}); | ||
} | ||
return accumulator; | ||
}; | ||
exports.createPlugin = function (config) { | ||
@@ -158,2 +120,40 @@ var _a = config || {}, _b = _a.componentChunkName, componentChunkName = _b === void 0 ? 'react-component' : _b, _c = _a.importChunkName, importChunkName = _c === void 0 ? 'import' : _c, _d = _a.styleChunkName, styleChunkName = _d === void 0 ? 'jss-style-definition' : _d, _e = _a.exportChunkName, exportChunkName = _e === void 0 ? 'export' : _e, _f = _a.jssDeclarationName, jssDeclarationName = _f === void 0 ? 'style' : _f; | ||
exports.default = exports.createPlugin(); | ||
var prepareDynamicProps = function (styles) { | ||
return Object.keys(styles).reduce(function (acc, key) { | ||
var value = styles[key]; | ||
if (typeof value === 'string' && value.startsWith('$props.')) { | ||
acc[key] = new js_ast_1.ParsedASTNode(t.arrowFunctionExpression([t.identifier('props')], t.memberExpression(t.identifier('props'), t.identifier(value.replace('$props.', ''))))); | ||
} | ||
else { | ||
acc[key] = styles[key]; | ||
} | ||
return acc; | ||
}, {}); | ||
}; | ||
var generateStyleTagStrings = function (content, nodesLookup) { | ||
var accumulator = {}; | ||
var styles = content.styles, children = content.children, key = content.key, repeat = content.repeat; | ||
if (styles) { | ||
var root = nodesLookup[key]; | ||
var className = helpers_1.cammelCaseToDashCase(key); | ||
accumulator[className] = prepareDynamicProps(styles); | ||
// addClassStringOnJSXTag(root.node, className) | ||
jsx_ast_1.addDynamicPropOnJsxOpeningTag(root, 'className', "classes['" + className + "']", 'props'); | ||
} | ||
if (repeat) { | ||
var items = generateStyleTagStrings(repeat.content, nodesLookup); | ||
accumulator = __assign({}, accumulator, items); | ||
} | ||
if (children) { | ||
children.forEach(function (child) { | ||
if (typeof child === 'string') { | ||
return; | ||
} | ||
// only call on children if they are not strings | ||
var items = generateStyleTagStrings(child, nodesLookup); | ||
accumulator = __assign({}, accumulator, items); | ||
}); | ||
} | ||
return accumulator; | ||
}; | ||
//# sourceMappingURL=react-jss.js.map |
@@ -63,2 +63,3 @@ "use strict"; | ||
path: 'prop-types', | ||
version: '15.7.2', | ||
}; | ||
@@ -65,0 +66,0 @@ chunks.push({ |
@@ -47,2 +47,30 @@ "use strict"; | ||
var helpers_1 = require("../../utils/helpers"); | ||
exports.createPlugin = function (config) { | ||
var _a = (config || {}).componentChunkName, componentChunkName = _a === void 0 ? 'react-component' : _a; | ||
var reactStyledJSXChunkPlugin = function (structure) { return __awaiter(_this, void 0, void 0, function () { | ||
var uidl, chunks, content, componentChunk, jsxNodesLookup, styleJSXString, jsxASTNodeReference, rootJSXNode; | ||
return __generator(this, function (_a) { | ||
uidl = structure.uidl, chunks = structure.chunks; | ||
content = uidl.content; | ||
componentChunk = chunks.find(function (chunk) { return chunk.name === componentChunkName; }); | ||
if (!componentChunk) { | ||
return [2 /*return*/, structure]; | ||
} | ||
jsxNodesLookup = componentChunk.meta.nodesLookup; | ||
styleJSXString = generateStyledJSXString(content, jsxNodesLookup); | ||
if (!styleJSXString || !styleJSXString.length) { | ||
return [2 /*return*/, structure]; | ||
} | ||
jsxASTNodeReference = jsx_ast_1.generateStyledJSXTag(styleJSXString.join('\n')); | ||
rootJSXNode = jsxNodesLookup[content.key]; | ||
// We have the ability to insert the tag into the existig JSX structure, or do something else with it. | ||
// Here we take the JSX <style> tag and we insert it as the last child of the JSX structure | ||
// inside the React Component | ||
rootJSXNode.children.push(jsxASTNodeReference); | ||
return [2 /*return*/, structure]; | ||
}); | ||
}); }; | ||
return reactStyledJSXChunkPlugin; | ||
}; | ||
exports.default = exports.createPlugin(); | ||
var prepareDynamicProps = function (styles) { | ||
@@ -103,30 +131,2 @@ return Object.keys(styles).reduce(function (acc, key) { | ||
}; | ||
exports.createPlugin = function (config) { | ||
var _a = (config || {}).componentChunkName, componentChunkName = _a === void 0 ? 'react-component' : _a; | ||
var reactStyledJSXChunkPlugin = function (structure) { return __awaiter(_this, void 0, void 0, function () { | ||
var uidl, chunks, content, componentChunk, jsxNodesLookup, styleJSXString, jsxASTNodeReference, rootJSXNode; | ||
return __generator(this, function (_a) { | ||
uidl = structure.uidl, chunks = structure.chunks; | ||
content = uidl.content; | ||
componentChunk = chunks.find(function (chunk) { return chunk.name === componentChunkName; }); | ||
if (!componentChunk) { | ||
return [2 /*return*/, structure]; | ||
} | ||
jsxNodesLookup = componentChunk.meta.nodesLookup; | ||
styleJSXString = generateStyledJSXString(content, jsxNodesLookup); | ||
if (!styleJSXString || !styleJSXString.length) { | ||
return [2 /*return*/, structure]; | ||
} | ||
jsxASTNodeReference = jsx_ast_1.generateStyledJSXTag(styleJSXString.join('\n')); | ||
rootJSXNode = jsxNodesLookup[content.key]; | ||
// We have the ability to insert the tag into the existig JSX structure, or do something else with it. | ||
// Here we take the JSX <style> tag and we insert it as the last child of the JSX structure | ||
// inside the React Component | ||
rootJSXNode.children.push(jsxASTNodeReference); | ||
return [2 /*return*/, structure]; | ||
}); | ||
}); }; | ||
return reactStyledJSXChunkPlugin; | ||
}; | ||
exports.default = exports.createPlugin(); | ||
//# sourceMappingURL=react-styled-jsx.js.map |
@@ -46,2 +46,31 @@ "use strict"; | ||
var helpers_1 = require("../../utils/helpers"); | ||
exports.createPlugin = function (config) { | ||
var _a = config || {}, _b = _a.chunkName, chunkName = _b === void 0 ? 'vue-component-style-chunk' : _b, _c = _a.vueTemplateChunk, vueTemplateChunk = _c === void 0 ? 'vue-component-template-chunk' : _c, _d = _a.styleFileId, styleFileId = _d === void 0 ? null : _d; | ||
var vueComponentStyleChunkPlugin = function (structure) { return __awaiter(_this, void 0, void 0, function () { | ||
var uidl, chunks, content, templateChunk, templateLookup, jssStylesArray; | ||
return __generator(this, function (_a) { | ||
uidl = structure.uidl, chunks = structure.chunks; | ||
content = uidl.content; | ||
templateChunk = chunks.filter(function (chunk) { return chunk.name === vueTemplateChunk; })[0]; | ||
templateLookup = templateChunk.meta.lookup; | ||
jssStylesArray = generateStyleTagStrings(content, templateLookup); | ||
chunks.push({ | ||
type: 'string', | ||
name: chunkName, | ||
meta: { | ||
fileId: styleFileId, | ||
}, | ||
wrap: styleFileId | ||
? undefined | ||
: function (generatedContent) { | ||
return "<style>\n" + generatedContent + "</style>"; | ||
}, | ||
content: jssStylesArray.join('\n'), | ||
}); | ||
return [2 /*return*/, structure]; | ||
}); | ||
}); }; | ||
return vueComponentStyleChunkPlugin; | ||
}; | ||
exports.default = exports.createPlugin(); | ||
var filterOutDynamicStyles = function (styles) { | ||
@@ -98,31 +127,2 @@ if (!styles) { | ||
}; | ||
exports.createPlugin = function (config) { | ||
var _a = config || {}, _b = _a.chunkName, chunkName = _b === void 0 ? 'vue-component-style-chunk' : _b, _c = _a.vueTemplateChunk, vueTemplateChunk = _c === void 0 ? 'vue-component-template-chunk' : _c, _d = _a.styleFileId, styleFileId = _d === void 0 ? null : _d; | ||
var vueComponentStyleChunkPlugin = function (structure) { return __awaiter(_this, void 0, void 0, function () { | ||
var uidl, chunks, content, templateChunk, templateLookup, jssStylesArray; | ||
return __generator(this, function (_a) { | ||
uidl = structure.uidl, chunks = structure.chunks; | ||
content = uidl.content; | ||
templateChunk = chunks.filter(function (chunk) { return chunk.name === vueTemplateChunk; })[0]; | ||
templateLookup = templateChunk.meta.lookup; | ||
jssStylesArray = generateStyleTagStrings(content, templateLookup); | ||
chunks.push({ | ||
type: 'string', | ||
name: chunkName, | ||
meta: { | ||
fileId: styleFileId, | ||
}, | ||
wrap: styleFileId | ||
? undefined | ||
: function (generatedContent) { | ||
return "<style>\n" + generatedContent + "</style>"; | ||
}, | ||
content: jssStylesArray.join('\n'), | ||
}); | ||
return [2 /*return*/, structure]; | ||
}); | ||
}); }; | ||
return vueComponentStyleChunkPlugin; | ||
}; | ||
exports.default = exports.createPlugin(); | ||
//# sourceMappingURL=vue-style-chunk.js.map |
@@ -51,4 +51,5 @@ "use strict"; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var _a; | ||
var _this = this; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var pipeline_1 = require("../pipeline"); | ||
@@ -63,4 +64,4 @@ var react_base_component_1 = require("../plugins/react/react-base-component"); | ||
var html_mapping_json_1 = __importDefault(require("../../uidl-definitions/elements-mapping/html-mapping.json")); | ||
var elements_mapping_json_1 = __importDefault(require("./elements-mapping.json")); | ||
var utils_1 = require("./utils"); | ||
var react_mapping_json_1 = __importDefault(require("./react-mapping.json")); | ||
var types_1 = require("../types"); | ||
var configuredReactJSX = react_base_component_1.createPlugin({ | ||
@@ -91,8 +92,8 @@ componentChunkName: 'react-component', | ||
}); | ||
var stylePlugins = { | ||
InlineStyles: configuredReactInlineStyles, | ||
StyledJSX: configuredReactStyledJSX, | ||
JSS: configuredReactJSS, | ||
CSSModules: configuredReactCSSModules, | ||
}; | ||
var stylePlugins = (_a = {}, | ||
_a[types_1.ReactComponentStylingFlavors.InlineStyles] = configuredReactInlineStyles, | ||
_a[types_1.ReactComponentStylingFlavors.StyledJSX] = configuredReactStyledJSX, | ||
_a[types_1.ReactComponentStylingFlavors.JSS] = configuredReactJSS, | ||
_a[types_1.ReactComponentStylingFlavors.CSSModules] = configuredReactCSSModules, | ||
_a); | ||
/** | ||
@@ -113,4 +114,4 @@ * Runs a component pipeline and generates all the necessary parts (ex: js, css) as well as the list of dependencies | ||
stylePlugin = stylePlugins[variation]; | ||
resolver = new pipeline_1.Resolver(__assign({}, html_mapping_json_1.default, elements_mapping_json_1.default, customMapping)); | ||
assemblyLine = new pipeline_1.ComponentAssemblyLine([ | ||
resolver = new pipeline_1.Resolver(__assign({}, html_mapping_json_1.default, react_mapping_json_1.default, customMapping)); | ||
assemblyLine = new pipeline_1.AssemblyLine([ | ||
configuredReactJSX, | ||
@@ -126,3 +127,3 @@ stylePlugin, | ||
result = _a.sent(); | ||
chunksByFileId = utils_1.groupChunksByFileId(result.chunks); | ||
chunksByFileId = assemblyLine.groupChunksByFileId(result.chunks); | ||
code = chunksLinker.link(chunksByFileId.default); | ||
@@ -129,0 +130,0 @@ css = chunksLinker.link(chunksByFileId['component-styles']); |
@@ -1,5 +0,5 @@ | ||
import { ReactComponentFlavors, ComponentGenerator } from '../types'; | ||
import { ReactComponentStylingFlavors, ComponentGenerator } from '../types'; | ||
import { ElementsMapping } from '../../uidl-definitions/types'; | ||
interface ReactGeneratorFactoryParams { | ||
variation?: ReactComponentFlavors; | ||
variation?: ReactComponentStylingFlavors; | ||
customMapping?: ElementsMapping; | ||
@@ -6,0 +6,0 @@ } |
@@ -52,34 +52,11 @@ "use strict"; | ||
var html_mapping_json_1 = __importDefault(require("../../uidl-definitions/elements-mapping/html-mapping.json")); | ||
var elements_mapping_json_1 = __importDefault(require("./elements-mapping.json")); | ||
var utils_1 = require("./utils"); | ||
var chooseStylePlugin = function (variation) { | ||
switch (variation) { | ||
case types_1.ReactComponentFlavors.CSSModules: | ||
return react_css_modules_1.createPlugin({ | ||
componentChunkName: 'react-component', | ||
}); | ||
case types_1.ReactComponentFlavors.InlineStyles: | ||
return react_inline_styles_1.createPlugin({ | ||
componentChunkName: 'react-component', | ||
}); | ||
case types_1.ReactComponentFlavors.JSS: | ||
return react_jss_1.createPlugin({ | ||
componentChunkName: 'react-component', | ||
importChunkName: 'import', | ||
exportChunkName: 'export', | ||
}); | ||
case types_1.ReactComponentFlavors.StyledJSX: | ||
return react_styled_jsx_1.createPlugin({ | ||
componentChunkName: 'react-component', | ||
}); | ||
} | ||
}; | ||
var react_mapping_json_1 = __importDefault(require("./react-mapping.json")); | ||
var createReactGenerator = function (params) { | ||
if (params === void 0) { params = {}; } | ||
var _a = params.variation, variation = _a === void 0 ? types_1.ReactComponentFlavors.CSSModules : _a, _b = params.customMapping, customMapping = _b === void 0 ? {} : _b; | ||
var _a = params.variation, variation = _a === void 0 ? types_1.ReactComponentStylingFlavors.InlineStyles : _a, _b = params.customMapping, customMapping = _b === void 0 ? {} : _b; | ||
var resolver = new pipeline_1.Resolver(); | ||
resolver.addMapping(html_mapping_json_1.default); | ||
resolver.addMapping(elements_mapping_json_1.default); | ||
resolver.addMapping(react_mapping_json_1.default); | ||
resolver.addMapping(customMapping); | ||
var assemblyLine = new pipeline_1.ComponentAssemblyLine(); | ||
var assemblyLine = new pipeline_1.AssemblyLine(); | ||
assemblyLine.addPlugin(react_base_component_1.createPlugin({ | ||
@@ -109,3 +86,3 @@ componentChunkName: 'react-component', | ||
result = _a.sent(); | ||
chunksByFileId = utils_1.groupChunksByFileId(result.chunks); | ||
chunksByFileId = assemblyLine.groupChunksByFileId(result.chunks); | ||
code = chunksLinker.link(chunksByFileId.default); | ||
@@ -129,3 +106,29 @@ externalCSS = chunksLinker.link(chunksByFileId['component-styles']); | ||
}; | ||
var chooseStylePlugin = function (variation) { | ||
switch (variation) { | ||
case types_1.ReactComponentStylingFlavors.CSSModules: | ||
return react_css_modules_1.createPlugin({ | ||
componentChunkName: 'react-component', | ||
}); | ||
case types_1.ReactComponentStylingFlavors.InlineStyles: | ||
return react_inline_styles_1.createPlugin({ | ||
componentChunkName: 'react-component', | ||
}); | ||
case types_1.ReactComponentStylingFlavors.JSS: | ||
return react_jss_1.createPlugin({ | ||
componentChunkName: 'react-component', | ||
importChunkName: 'import', | ||
exportChunkName: 'export', | ||
}); | ||
case types_1.ReactComponentStylingFlavors.StyledJSX: | ||
return react_styled_jsx_1.createPlugin({ | ||
componentChunkName: 'react-component', | ||
}); | ||
default: | ||
return react_inline_styles_1.createPlugin({ | ||
componentChunkName: 'react-component', | ||
}); | ||
} | ||
}; | ||
exports.default = createReactGenerator; | ||
//# sourceMappingURL=react-component.js.map |
@@ -57,5 +57,5 @@ "use strict"; | ||
var html_mapping_json_1 = __importDefault(require("../../uidl-definitions/elements-mapping/html-mapping.json")); | ||
var elements_mapping_json_1 = __importDefault(require("./elements-mapping.json")); | ||
var react_mapping_json_1 = __importDefault(require("./react-mapping.json")); | ||
var createRouterComponentGenerator = function () { | ||
var resolver = new pipeline_1.Resolver(__assign({}, html_mapping_json_1.default, elements_mapping_json_1.default)); | ||
var resolver = new pipeline_1.Resolver(__assign({}, html_mapping_json_1.default, react_mapping_json_1.default)); | ||
var configureAppRouterComponent = react_app_routing_1.createPlugin({ | ||
@@ -69,6 +69,3 @@ componentChunkName: 'app-router-component', | ||
}); | ||
var assemblyLine = new pipeline_1.ComponentAssemblyLine([ | ||
configureAppRouterComponent, | ||
configureImportStatements, | ||
]); | ||
var assemblyLine = new pipeline_1.AssemblyLine([configureAppRouterComponent, configureImportStatements]); | ||
var chunksLinker = new pipeline_1.Builder(); | ||
@@ -75,0 +72,0 @@ var generateComponent = function (uidl) { return __awaiter(_this, void 0, void 0, function () { |
@@ -94,2 +94,14 @@ "use strict"; | ||
}; | ||
exports.addAttributeToJSXTag = function (jsxNode, attribute, t) { | ||
if (t === void 0) { t = types; } | ||
var nameOfAttribute = t.jsxIdentifier(attribute.name); | ||
var attributeDefinition; | ||
if (typeof attribute.value === 'boolean') { | ||
attributeDefinition = t.jsxAttribute(nameOfAttribute); | ||
} | ||
else { | ||
attributeDefinition = t.jsxAttribute(nameOfAttribute, getProperAttributeValueAssignment(attribute.value)); | ||
} | ||
jsxNode.openingElement.attributes.push(attributeDefinition); | ||
}; | ||
/** | ||
@@ -118,14 +130,2 @@ * node must be a AST node element of type JSXElement (babel-types) or | ||
}; | ||
exports.addAttributeToJSXTag = function (jsxNode, attribute, t) { | ||
if (t === void 0) { t = types; } | ||
var nameOfAttribute = t.jsxIdentifier(attribute.name); | ||
var attributeDefinition; | ||
if (typeof attribute.value === 'boolean') { | ||
attributeDefinition = t.jsxAttribute(nameOfAttribute); | ||
} | ||
else { | ||
attributeDefinition = t.jsxAttribute(nameOfAttribute, getProperAttributeValueAssignment(attribute.value)); | ||
} | ||
jsxNode.openingElement.attributes.push(attributeDefinition); | ||
}; | ||
/** | ||
@@ -132,0 +132,0 @@ * Generates the AST definiton (without start/end position) for a JSX tag |
@@ -15,1 +15,2 @@ import { StateDefinition } from '../../uidl-definitions/types'; | ||
}; | ||
export declare const prefixPlaygroundAssetsURL: (prefix: string, originalString: string) => string; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var constants_1 = require("../constants"); | ||
/** | ||
@@ -36,2 +37,11 @@ * A couple of different cases which need to be handled | ||
}; | ||
exports.prefixPlaygroundAssetsURL = function (prefix, originalString) { | ||
if (!originalString || !originalString.startsWith(constants_1.ASSETS_IDENTIFIER)) { | ||
return originalString; | ||
} | ||
if (originalString.startsWith('/')) { | ||
return prefix + originalString; | ||
} | ||
return prefix + "/" + originalString; | ||
}; | ||
//# sourceMappingURL=uidl-utils.js.map |
@@ -58,7 +58,7 @@ "use strict"; | ||
var html_mapping_json_1 = __importDefault(require("../../uidl-definitions/elements-mapping/html-mapping.json")); | ||
var elements_mapping_json_1 = __importDefault(require("./elements-mapping.json")); | ||
var vue_mapping_json_1 = __importDefault(require("./vue-mapping.json")); | ||
var createVueGenerator = function (_a) { | ||
var customMapping = (_a === void 0 ? { customMapping: {} } : _a).customMapping; | ||
var resolver = new pipeline_1.Resolver(__assign({}, html_mapping_json_1.default, elements_mapping_json_1.default, customMapping)); | ||
var assemblyLine = new pipeline_1.ComponentAssemblyLine([ | ||
var resolver = new pipeline_1.Resolver(__assign({}, html_mapping_json_1.default, vue_mapping_json_1.default, customMapping)); | ||
var assemblyLine = new pipeline_1.AssemblyLine([ | ||
vue_base_component_1.createPlugin({ | ||
@@ -65,0 +65,0 @@ jsFileId: 'vuejs', |
@@ -57,7 +57,7 @@ "use strict"; | ||
var html_mapping_json_1 = __importDefault(require("../../uidl-definitions/elements-mapping/html-mapping.json")); | ||
var elements_mapping_json_1 = __importDefault(require("./elements-mapping.json")); | ||
var vue_mapping_json_1 = __importDefault(require("./vue-mapping.json")); | ||
var createVuePipeline = function (_a) { | ||
var customMapping = (_a === void 0 ? {} : _a).customMapping; | ||
var resolver = new pipeline_1.Resolver(__assign({}, html_mapping_json_1.default, elements_mapping_json_1.default, customMapping)); | ||
var assemblyLine = new pipeline_1.ComponentAssemblyLine([ | ||
var resolver = new pipeline_1.Resolver(__assign({}, html_mapping_json_1.default, vue_mapping_json_1.default, customMapping)); | ||
var assemblyLine = new pipeline_1.AssemblyLine([ | ||
vue_router_1.createPlugin({ | ||
@@ -64,0 +64,0 @@ codeChunkName: 'vue-router', |
@@ -53,3 +53,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var elements_mapping_json_1 = __importDefault(require("./elements-mapping.json")); | ||
var react_project_mapping_json_1 = __importDefault(require("./react-project-mapping.json")); | ||
var react_router_1 = __importDefault(require("../../component-generators/react/react-router")); | ||
@@ -61,6 +61,7 @@ var react_component_1 = __importDefault(require("../../component-generators/react/react-component")); | ||
var utils_1 = require("./utils"); | ||
var constants_1 = require("./constants"); | ||
exports.default = (function (uidl, options) { | ||
if (options === void 0) { options = {}; } | ||
return __awaiter(_this, void 0, void 0, function () { | ||
var reactGenerator, routingComponentGenerator, componentsFolder, pagesFolder, staticFolder, srcFolder, distFolder, allDependencies, _a, components, root, states, stateDefinitions, assetsPrefix, result, routerDefinitions, manifestJSON, manifestFile, htmlIndexContent, htmlFile, rootCopy, routingComponent, sourcePackageJson, externalDep, packageFile; | ||
var reactGenerator, routingComponentGenerator, componentsFolder, pagesFolder, staticFolder, srcFolder, distFolder, allDependencies, _a, components, root, states, stateDefinitions, result, routerDefinitions, manifestJSON, manifestFile, htmlIndexContent, htmlFile, rootCopy, routingComponent, sourcePackageJson, packageJSON, packageFile; | ||
var _this = this; | ||
@@ -71,3 +72,3 @@ return __generator(this, function (_b) { | ||
reactGenerator = react_component_1.default({ | ||
variation: types_1.ReactComponentFlavors.CSSModules, | ||
variation: types_1.ReactComponentStylingFlavors.CSSModules, | ||
}); | ||
@@ -96,3 +97,3 @@ routingComponentGenerator = react_router_1.default(); | ||
distFolder = { | ||
name: options.distPath || 'dist', | ||
name: options.distPath || constants_1.DEFAULT_OUTPUT_FOLDER, | ||
files: [], | ||
@@ -102,3 +103,3 @@ subFolders: [srcFolder], | ||
// Step 2: Initialization with project specific mappings and of other data structures | ||
reactGenerator.addMapping(elements_mapping_json_1.default); | ||
reactGenerator.addMapping(react_project_mapping_json_1.default); | ||
if (options.customMapping) { | ||
@@ -111,6 +112,5 @@ reactGenerator.addMapping(options.customMapping); | ||
stateDefinitions = root.stateDefinitions; | ||
assetsPrefix = '/static'; | ||
result = { | ||
outputFolder: distFolder, | ||
assetsPath: assetsPrefix, | ||
assetsPath: 'src' + constants_1.ASSETS_PREFIX, | ||
}; | ||
@@ -126,3 +126,3 @@ if (!states || !stateDefinitions) { | ||
if (uidl.globals.manifest) { | ||
manifestJSON = generator_utils_1.createManifestJSON(uidl.globals.manifest, uidl.name); | ||
manifestJSON = generator_utils_1.createManifestJSON(uidl.globals.manifest, uidl.name, constants_1.ASSETS_PREFIX); | ||
manifestFile = { | ||
@@ -174,2 +174,3 @@ name: 'manifest', | ||
localDependenciesPrefix: '../components/', | ||
assetsPrefix: constants_1.ASSETS_PREFIX, | ||
})]; | ||
@@ -210,3 +211,5 @@ case 1: | ||
component = components[componentName]; | ||
return [4 /*yield*/, reactGenerator.generateComponent(component)]; | ||
return [4 /*yield*/, reactGenerator.generateComponent(component, { | ||
assetsPrefix: constants_1.ASSETS_PREFIX, | ||
})]; | ||
case 1: | ||
@@ -240,12 +243,11 @@ compiledComponent = _a.sent(); | ||
sourcePackageJson = options.sourcePackageJson; | ||
if (sourcePackageJson) { | ||
externalDep = generator_utils_1.extractExternalDependencies(allDependencies); | ||
sourcePackageJson.dependencies = __assign({}, sourcePackageJson.dependencies, externalDep); | ||
packageFile = { | ||
name: 'package', | ||
extension: '.json', | ||
content: JSON.stringify(sourcePackageJson, null, 2), | ||
}; | ||
distFolder.files.push(packageFile); | ||
} | ||
packageJSON = generator_utils_1.createPackageJSON(sourcePackageJson || constants_1.DEFAULT_PACKAGE_JSON, allDependencies, { | ||
projectName: uidl.name, | ||
}); | ||
packageFile = { | ||
name: 'package', | ||
extension: '.json', | ||
content: JSON.stringify(packageJSON, null, 2), | ||
}; | ||
distFolder.files.push(packageFile); | ||
return [2 /*return*/, result]; | ||
@@ -252,0 +254,0 @@ } |
@@ -5,2 +5,4 @@ "use strict"; | ||
var xml_1 = require("../../component-generators/utils/xml"); | ||
var constants_1 = require("./constants"); | ||
var uidl_utils_1 = require("../../component-generators/utils/uidl-utils"); | ||
exports.createHtmlIndexFile = function (uidl) { | ||
@@ -34,3 +36,4 @@ var _a = uidl.globals, settings = _a.settings, meta = _a.meta, assets = _a.assets, manifest = _a.manifest; | ||
Object.keys(metaItem).forEach(function (key) { | ||
metaTag.attr(key, metaItem[key]); | ||
var prefixedURL = uidl_utils_1.prefixPlaygroundAssetsURL(constants_1.ASSETS_PREFIX, metaItem[key]); | ||
metaTag.attr(key, prefixedURL); | ||
}); | ||
@@ -40,7 +43,8 @@ headNode.append(metaTag); | ||
assets.forEach(function (asset) { | ||
var assetPath = uidl_utils_1.prefixPlaygroundAssetsURL(constants_1.ASSETS_PREFIX, asset.path); | ||
// link stylesheet (external css, font) | ||
if ((asset.type === 'style' || asset.type === 'font') && asset.path) { | ||
if ((asset.type === 'style' || asset.type === 'font') && assetPath) { | ||
var linkTag = xml_1.createXMLNode('link', { selfClosing: true }); | ||
linkTag.attr('rel', 'stylesheet'); | ||
linkTag.attr('href', asset.path); | ||
linkTag.attr('href', assetPath); | ||
headNode.append(linkTag); | ||
@@ -60,4 +64,4 @@ } | ||
scriptTag.attr('type', 'text/javascript'); | ||
if (asset.path) { | ||
scriptTag.attr('src', asset.path); | ||
if (assetPath) { | ||
scriptTag.attr('src', assetPath); | ||
if (asset.meta && asset.meta.defer) { | ||
@@ -81,6 +85,6 @@ scriptTag.attr('defer', true); | ||
// icon | ||
if (asset.type === 'icon' && asset.path) { | ||
if (asset.type === 'icon' && assetPath) { | ||
var iconTag_1 = xml_1.createXMLNode('link', { selfClosing: true }); | ||
iconTag_1.attr('rel', 'shortcut icon'); | ||
iconTag_1.attr('href', asset.path); | ||
iconTag_1.attr('href', assetPath); | ||
if (typeof asset.meta === 'object') { | ||
@@ -87,0 +91,0 @@ var assetMeta_1 = asset.meta; |
@@ -58,7 +58,8 @@ "use strict"; | ||
var utils_1 = require("./utils"); | ||
var elements_mapping_json_1 = __importDefault(require("./elements-mapping.json")); | ||
var next_mapping_json_1 = __importDefault(require("./next-mapping.json")); | ||
var constants_1 = require("./constants"); | ||
exports.default = (function (uidl, options) { | ||
if (options === void 0) { options = {}; } | ||
return __awaiter(_this, void 0, void 0, function () { | ||
var reactGenerator, pagesFolder, componentsFolder, staticFolder, distFolder, allDependencies, assetsPrefix, components, root, states, stateDefinitions, result, routerDefinitions, manifestJSON, manifestFile, documentComponent, file, sourcePackageJson, externalDep, packageFile; | ||
var reactGenerator, pagesFolder, componentsFolder, staticFolder, distFolder, allDependencies, components, root, states, stateDefinitions, result, routerDefinitions, manifestJSON, manifestFile, documentComponent, file, sourcePackageJson, packageJSON, packageFile; | ||
var _this = this; | ||
@@ -69,4 +70,4 @@ return __generator(this, function (_a) { | ||
reactGenerator = react_component_1.default({ | ||
variation: types_1.ReactComponentFlavors.StyledJSX, | ||
customMapping: elements_mapping_json_1.default, | ||
variation: types_1.ReactComponentStylingFlavors.StyledJSX, | ||
customMapping: next_mapping_json_1.default, | ||
}); | ||
@@ -89,3 +90,3 @@ pagesFolder = { | ||
distFolder = { | ||
name: options.distPath || 'dist', | ||
name: options.distPath || constants_1.DEFAULT_OUTPUT_FOLDER, | ||
files: [], | ||
@@ -95,3 +96,3 @@ subFolders: [pagesFolder, componentsFolder, staticFolder], | ||
// Step 2: Initialization with project specific mappings and of other data structures | ||
reactGenerator.addMapping(elements_mapping_json_1.default); | ||
reactGenerator.addMapping(next_mapping_json_1.default); | ||
if (options.customMapping) { | ||
@@ -101,3 +102,2 @@ reactGenerator.addMapping(options.customMapping); | ||
allDependencies = {}; | ||
assetsPrefix = '/static'; | ||
components = uidl.components, root = uidl.root; | ||
@@ -108,3 +108,3 @@ states = root.content.states; | ||
outputFolder: distFolder, | ||
assetsPath: assetsPrefix, | ||
assetsPath: constants_1.ASSETS_PREFIX.slice(1), | ||
}; | ||
@@ -120,3 +120,3 @@ if (!states || !stateDefinitions) { | ||
if (uidl.globals.manifest) { | ||
manifestJSON = generator_utils_1.createManifestJSON(uidl.globals.manifest, uidl.name); | ||
manifestJSON = generator_utils_1.createManifestJSON(uidl.globals.manifest, uidl.name, constants_1.ASSETS_PREFIX); | ||
manifestFile = { | ||
@@ -162,3 +162,3 @@ name: 'manifest', | ||
localDependenciesPrefix: '../components/', | ||
assetsPrefix: assetsPrefix, | ||
assetsPrefix: constants_1.ASSETS_PREFIX, | ||
})]; | ||
@@ -199,3 +199,3 @@ case 2: | ||
return [4 /*yield*/, reactGenerator.generateComponent(component, { | ||
assetsPrefix: assetsPrefix, | ||
assetsPrefix: constants_1.ASSETS_PREFIX, | ||
})]; | ||
@@ -225,12 +225,11 @@ case 2: | ||
sourcePackageJson = options.sourcePackageJson; | ||
if (sourcePackageJson) { | ||
externalDep = generator_utils_1.extractExternalDependencies(allDependencies); | ||
sourcePackageJson.dependencies = __assign({}, sourcePackageJson.dependencies, externalDep); | ||
packageFile = { | ||
name: 'package', | ||
extension: '.json', | ||
content: JSON.stringify(sourcePackageJson, null, 2), | ||
}; | ||
distFolder.files.push(packageFile); | ||
} | ||
packageJSON = generator_utils_1.createPackageJSON(sourcePackageJson || constants_1.DEFAULT_PACKAGE_JSON, allDependencies, { | ||
projectName: uidl.name, | ||
}); | ||
packageFile = { | ||
name: 'package', | ||
extension: '.json', | ||
content: JSON.stringify(packageJSON, null, 2), | ||
}; | ||
distFolder.files.push(packageFile); | ||
return [2 /*return*/, result]; | ||
@@ -237,0 +236,0 @@ } |
@@ -13,2 +13,4 @@ "use strict"; | ||
var types = __importStar(require("@babel/types")); | ||
var constants_1 = require("./constants"); | ||
var uidl_utils_1 = require("../../component-generators/utils/uidl-utils"); | ||
exports.createDocumentComponent = function (uidl, t) { | ||
@@ -43,3 +45,4 @@ if (t === void 0) { t = types; } | ||
Object.keys(metaItem).forEach(function (key) { | ||
jsx_ast_1.addAttributeToJSXTag(metaTag, { name: key, value: metaItem[key] }); | ||
var metaValue = uidl_utils_1.prefixPlaygroundAssetsURL(constants_1.ASSETS_PREFIX, metaItem[key]); | ||
jsx_ast_1.addAttributeToJSXTag(metaTag, { name: key, value: metaValue }); | ||
}); | ||
@@ -49,7 +52,8 @@ jsx_ast_1.addChildJSXTag(headNode, metaTag); | ||
assets.forEach(function (asset) { | ||
var assetPath = uidl_utils_1.prefixPlaygroundAssetsURL(constants_1.ASSETS_PREFIX, asset.path); | ||
// link stylesheet (external css, font) | ||
if ((asset.type === 'style' || asset.type === 'font') && asset.path) { | ||
if ((asset.type === 'style' || asset.type === 'font') && assetPath) { | ||
var linkTag = jsx_ast_1.generateASTDefinitionForJSXTag('link'); | ||
jsx_ast_1.addAttributeToJSXTag(linkTag, { name: 'rel', value: 'stylesheet' }); | ||
jsx_ast_1.addAttributeToJSXTag(linkTag, { name: 'href', value: asset.path }); | ||
jsx_ast_1.addAttributeToJSXTag(linkTag, { name: 'href', value: assetPath }); | ||
jsx_ast_1.addChildJSXTag(headNode, linkTag); | ||
@@ -70,4 +74,4 @@ } | ||
jsx_ast_1.addAttributeToJSXTag(scriptTag, { name: 'type', value: 'text/javascript' }); | ||
if (asset.path) { | ||
jsx_ast_1.addAttributeToJSXTag(scriptTag, { name: 'src', value: asset.path }); | ||
if (assetPath) { | ||
jsx_ast_1.addAttributeToJSXTag(scriptTag, { name: 'src', value: assetPath }); | ||
if (asset.meta && asset.meta.defer) { | ||
@@ -94,6 +98,6 @@ jsx_ast_1.addAttributeToJSXTag(scriptTag, { name: 'defer', value: true }); | ||
// icon | ||
if (asset.type === 'icon' && asset.path) { | ||
if (asset.type === 'icon' && assetPath) { | ||
var iconTag_1 = jsx_ast_1.generateASTDefinitionForJSXTag('link'); | ||
jsx_ast_1.addAttributeToJSXTag(iconTag_1, { name: 'rel', value: 'shortcut icon' }); | ||
jsx_ast_1.addAttributeToJSXTag(iconTag_1, { name: 'href', value: asset.path }); | ||
jsx_ast_1.addAttributeToJSXTag(iconTag_1, { name: 'href', value: assetPath }); | ||
if (typeof asset.meta === 'object') { | ||
@@ -100,0 +104,0 @@ var assetMeta_1 = asset.meta; |
@@ -13,3 +13,3 @@ import { ProjectUIDL, ElementsMapping } from '../../uidl-definitions/types'; | ||
export interface ProjectGeneratorOptions { | ||
sourcePackageJson?: Record<string, any>; | ||
sourcePackageJson?: PackageJSON; | ||
distPath?: string; | ||
@@ -22,1 +22,11 @@ customMapping?: ElementsMapping; | ||
}>; | ||
export interface PackageJSON { | ||
name: string; | ||
description: string; | ||
version: string; | ||
main: string; | ||
author: string; | ||
license: string; | ||
scripts?: Record<string, string>; | ||
dependencies?: Record<string, string>; | ||
} |
import { ComponentDependency, WebManifest } from '../../uidl-definitions/types'; | ||
import { PackageJSON } from '../../project-generators/types'; | ||
export declare const extractExternalDependencies: (dependencies: Record<string, ComponentDependency>) => any; | ||
export declare const createManifestJSON: (manifest: WebManifest, projectName?: string) => { | ||
short_name?: string; | ||
name?: string; | ||
icons?: { | ||
export declare const createManifestJSON: (manifest: WebManifest, projectName: string, assetsPrefix?: string) => { | ||
icons: { | ||
src: string; | ||
@@ -11,2 +10,4 @@ type: string; | ||
}[]; | ||
short_name?: string; | ||
name?: string; | ||
start_url?: string; | ||
@@ -19,1 +20,4 @@ background_color?: string; | ||
}; | ||
export declare const createPackageJSON: (packageJSONTemplate: PackageJSON, dependencies: Record<string, ComponentDependency>, overwrites: { | ||
projectName: string; | ||
}) => PackageJSON; |
@@ -14,3 +14,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
// Only package dependencies are needed for the package.json file | ||
var uidl_utils_1 = require("../../component-generators/utils/uidl-utils"); | ||
exports.extractExternalDependencies = function (dependencies) { | ||
@@ -30,3 +30,3 @@ return Object.keys(dependencies) | ||
// Creates a manifest json with the UIDL having priority over the default values | ||
exports.createManifestJSON = function (manifest, projectName) { | ||
exports.createManifestJSON = function (manifest, projectName, assetsPrefix) { | ||
var defaultManifest = { | ||
@@ -38,4 +38,13 @@ short_name: projectName, | ||
}; | ||
return __assign({}, defaultManifest, manifest); | ||
var icons = manifest.icons.map(function (icon) { | ||
var src = uidl_utils_1.prefixPlaygroundAssetsURL(assetsPrefix, icon.src); | ||
return __assign({}, icon, { src: src }); | ||
}); | ||
return __assign({}, defaultManifest, manifest, { icons: icons }); | ||
}; | ||
exports.createPackageJSON = function (packageJSONTemplate, dependencies, overwrites) { | ||
var projectName = overwrites.projectName; | ||
var externalDep = exports.extractExternalDependencies(dependencies); | ||
return __assign({}, packageJSONTemplate, { name: projectName, dependencies: __assign({}, packageJSONTemplate.dependencies, externalDep) }); | ||
}; | ||
//# sourceMappingURL=generator-utils.js.map |
@@ -57,3 +57,3 @@ "use strict"; | ||
var generator_utils_1 = require("../utils/generator-utils"); | ||
var elements_mapping_json_1 = __importDefault(require("./elements-mapping.json")); | ||
var vue_project_mapping_json_1 = __importDefault(require("./vue-project-mapping.json")); | ||
exports.default = (function (uidl, options) { | ||
@@ -68,3 +68,3 @@ if (options === void 0) { options = {}; } | ||
vueGenerator = vue_component_1.default({ | ||
customMapping: elements_mapping_json_1.default, | ||
customMapping: vue_project_mapping_json_1.default, | ||
}); | ||
@@ -71,0 +71,0 @@ vueRouterGenerator = vue_router_1.default(); |
@@ -56,3 +56,3 @@ "use strict"; | ||
var vue_component_1 = __importDefault(require("../../component-generators/vue/vue-component")); | ||
var elements_mapping_json_1 = __importDefault(require("./elements-mapping.json")); | ||
var nuxt_mapping_json_1 = __importDefault(require("./nuxt-mapping.json")); | ||
exports.default = (function (uidl, options) { | ||
@@ -67,3 +67,3 @@ if (options === void 0) { options = {}; } | ||
vueGenerator = vue_component_1.default({ | ||
customMapping: __assign({}, elements_mapping_json_1.default), | ||
customMapping: __assign({}, nuxt_mapping_json_1.default), | ||
}); | ||
@@ -70,0 +70,0 @@ pagesFolder = { |
@@ -141,7 +141,28 @@ { | ||
"video": { | ||
"type": "video" | ||
"type": "video", | ||
"children": [ | ||
"$children", | ||
"This browser does not support the video formats given" | ||
] | ||
}, | ||
"audio": { | ||
"type": "audio" | ||
"type": "audio", | ||
"children": [ | ||
"$children", | ||
"This browser does not support the audio formats given" | ||
] | ||
}, | ||
"picture": { | ||
"type": "picture", | ||
"children": [ | ||
"$children", | ||
"This browser does not support the image formats given" | ||
] | ||
}, | ||
"source": { | ||
"type": "source", | ||
"attrs": { | ||
"src": "$attrs.url" | ||
} | ||
}, | ||
"svg": { | ||
@@ -148,0 +169,0 @@ "type": "svg" |
@@ -10,8 +10,3 @@ export interface ProjectUIDL { | ||
meta: Array<Record<string, string>>; | ||
assets: Array<{ | ||
type: string; | ||
path?: string; | ||
content?: string; | ||
meta?: Record<string, any>; | ||
}>; | ||
assets: GlobalAsset[]; | ||
manifest?: WebManifest; | ||
@@ -23,2 +18,8 @@ variables?: Record<string, string>; | ||
} | ||
export interface GlobalAsset { | ||
type: string; | ||
path?: string; | ||
content?: string; | ||
meta?: Record<string, any>; | ||
} | ||
export interface ComponentUIDL { | ||
@@ -25,0 +26,0 @@ $schema?: string; |
@@ -11,25 +11,25 @@ { | ||
"author": "teleportHQ", | ||
"license": "ISC", | ||
"license": "MIT", | ||
"dependencies": { | ||
"@babel/core": "7.1.6", | ||
"@babel/plugin-syntax-jsx": "7.0.0", | ||
"@babel/plugin-transform-react-display-name": "7.0.0", | ||
"@babel/plugin-transform-react-jsx": "7.1.6", | ||
"@babel/preset-env": "7.1.6", | ||
"react": "16.8.3", | ||
"react-dom": "16.8.3", | ||
"react-router-dom": "4.3.1" | ||
}, | ||
"devDependencies": { | ||
"@babel/core": "7.3.3", | ||
"@babel/plugin-syntax-jsx": "7.2.0", | ||
"@babel/plugin-transform-react-display-name": "7.2.0", | ||
"@babel/plugin-transform-react-jsx": "7.3.0", | ||
"@babel/preset-env": "7.3.1", | ||
"babel": "6.23.0", | ||
"babel-loader": "8.0.4", | ||
"clean-webpack-plugin": "1.0.0", | ||
"copy-webpack-plugin": "4.6.0", | ||
"babel-loader": "8.0.5", | ||
"clean-webpack-plugin": "1.0.1", | ||
"copy-webpack-plugin": "5.0.0", | ||
"html-webpack-plugin": "3.2.0", | ||
"react": "16.8.1", | ||
"react-dom": "16.8.1", | ||
"react-router-dom": "4.3.1", | ||
"webpack": "4.26.1" | ||
}, | ||
"devDependencies": { | ||
"css-loader": "2.0.0", | ||
"css-loader": "2.1.0", | ||
"style-loader": "0.23.1", | ||
"webpack-cli": "3.1.2", | ||
"webpack-dev-server": "3.1.10" | ||
"webpack-cli": "3.2.3", | ||
"webpack-dev-server": "3.2.0", | ||
"webpack": "4.29.5" | ||
} | ||
} |
@@ -10,9 +10,9 @@ { | ||
"dependencies": { | ||
"vue": "^2.5.17", | ||
"vue-router": "^3.0.1" | ||
"vue": "^2.6.7", | ||
"vue-router": "^3.0.2" | ||
}, | ||
"devDependencies": { | ||
"@vue/cli-plugin-babel": "^3.2.0", | ||
"@vue/cli-service": "^3.2.0", | ||
"vue-template-compiler": "^2.5.17" | ||
"@vue/cli-plugin-babel": "^3.4.1", | ||
"@vue/cli-service": "^3.4.1", | ||
"vue-template-compiler": "^2.6.7" | ||
}, | ||
@@ -19,0 +19,0 @@ "postcss": { |
@@ -15,10 +15,10 @@ { | ||
"cross-env": "^5.2.0", | ||
"nuxt": "^2.0.0" | ||
"nuxt": "^2.4.3" | ||
}, | ||
"devDependencies": { | ||
"nodemon": "^1.11.0", | ||
"eslint-config-prettier": "^3.1.0", | ||
"eslint-plugin-prettier": "2.6.2", | ||
"prettier": "1.14.3" | ||
"nodemon": "^1.18.10", | ||
"eslint-config-prettier": "^4.0.0", | ||
"eslint-plugin-prettier": "3.0.1", | ||
"prettier": "1.16.4" | ||
} | ||
} |
{ | ||
"$schema": "https://raw.githubusercontent.com/teleporthq/uidl-definitions/master/component.json", | ||
"$schema": "https://raw.githubusercontent.com/teleporthq/teleport-code-generators/master/src/uidl-definitions/schemas/component.json", | ||
"name": "AuthorCard", | ||
@@ -4,0 +4,0 @@ "propDefinitions": { |
{ | ||
"$schema": "https://raw.githubusercontent.com/teleporthq/uidl-definitions/master/component.json", | ||
"$schema": "https://raw.githubusercontent.com/teleporthq/teleport-code-generators/master/src/uidl-definitions/schemas/component.json", | ||
"name": "Cards", | ||
@@ -4,0 +4,0 @@ "propDefinitions": { |
{ | ||
"$schema": "https://raw.githubusercontent.com/teleporthq/uidl-definitions/master/component.json", | ||
"$schema": "https://raw.githubusercontent.com/teleporthq/teleport-code-generators/master/src/uidl-definitions/schemas/component.json", | ||
"name": "TabSelector", | ||
@@ -4,0 +4,0 @@ "stateDefinitions": { |
{ | ||
"$schema": "https://raw.githubusercontent.com/teleporthq/uidl-definitions/master/project.json", | ||
"$schema": "https://raw.githubusercontent.com/teleporthq/teleport-code-generators/master/src/uidl-definitions/schemas/project.json", | ||
"name": "myVueProject", | ||
@@ -4,0 +4,0 @@ "globals": { |
{ | ||
"$schema": "https://raw.githubusercontent.com/teleporthq/uidl-definitions/master/project.json", | ||
"$schema": "https://raw.githubusercontent.com/teleporthq/teleport-code-generators/master/src/uidl-definitions/schemas/project.json", | ||
"name": "Teleport Project", | ||
@@ -7,3 +7,3 @@ "globals": { | ||
"language": "en", | ||
"title": "TeleportHQ" | ||
"title": "teleportHQ" | ||
}, | ||
@@ -14,3 +14,4 @@ "meta" : [ | ||
{ "name": "viewport", "content": "width=device-width, initial-scale=1.0" }, | ||
{ "property": "og:title", "content": "Free Web tutorials" } | ||
{ "property": "og:title", "content": "Free Web tutorials" }, | ||
{ "property": "og:url", "content": "/playground_assets/asdasd.png" } | ||
], | ||
@@ -20,3 +21,3 @@ "manifest": { | ||
{ | ||
"src": "/static/icons-192.png", | ||
"src": "/playground_assets/icons-192.png", | ||
"type": "image/png", | ||
@@ -26,3 +27,3 @@ "sizes": "192x192" | ||
{ | ||
"src": "/static/icons-512.png", | ||
"src": "/playground_assets/icons-512.png", | ||
"type": "image/png", | ||
@@ -67,3 +68,3 @@ "sizes": "512x512" | ||
"type": "icon", | ||
"path": "/static/favicon-32x32.png", | ||
"path": "/playground_assets/favicon-32x32.png", | ||
"meta": { | ||
@@ -109,2 +110,27 @@ "type": "image/png", | ||
{ | ||
"type": "video", | ||
"key": "video", | ||
"attrs": { | ||
"autoPlay": true | ||
}, | ||
"children": [ | ||
{ | ||
"type": "source", | ||
"key": "mp4-source", | ||
"attrs": { | ||
"type": "video/mp4", | ||
"url": "https://www.quirksmode.org/html5/videos/big_buck_bunny.mp4" | ||
} | ||
}, | ||
{ | ||
"type": "source", | ||
"key": "webm-source", | ||
"attrs": { | ||
"type": "video/webm", | ||
"url": "https://www.quirksmode.org/html5/videos/big_buck_bunny.webm" | ||
} | ||
} | ||
] | ||
}, | ||
{ | ||
"type": "container", | ||
@@ -590,16 +616,2 @@ "key": "accordions", | ||
{ | ||
"type": "image", | ||
"key": "image", | ||
"attrs": { | ||
"url": "/playground_assets/image.jpg" | ||
} | ||
}, | ||
{ | ||
"type": "container", | ||
"key": "container", | ||
"styles": { | ||
"background": "#ffffff url(\"/playground_assets/image.png\") no-repeat right top;" | ||
} | ||
}, | ||
{ | ||
"type": "button", | ||
@@ -606,0 +618,0 @@ "key": "close", |
{ | ||
"$schema": "https://raw.githubusercontent.com/teleporthq/uidl-definitions/master/project.json", | ||
"$schema": "https://raw.githubusercontent.com/teleporthq/teleport-code-generators/master/src/uidl-definitions/schemas/project.json", | ||
"name": "myVueProject", | ||
@@ -4,0 +4,0 @@ "root": { |
{ | ||
"name": "@teleporthq/teleport-code-generators", | ||
"version": "0.3.0", | ||
"version": "0.3.1", | ||
"description": "Code generators and UIDL definition library", | ||
@@ -17,2 +17,3 @@ "main": "dist/index.js", | ||
"prepare": "npm run build", | ||
"add-contributor": "all-contributors add", | ||
"create-react-next": "ts-node --project tsconfig.json ./examples/project-exporters/react-next/index.ts", | ||
@@ -36,5 +37,6 @@ "create-react-basic": "ts-node --project tsconfig.json ./examples/project-exporters/react-basic/index.ts", | ||
"devDependencies": { | ||
"@types/cheerio": "^0.22.10", | ||
"@types/jest": "^24.0.6", | ||
"@types/cheerio": "^0.22.10", | ||
"@types/node": "^11.9.4", | ||
"all-contributors-cli": "^6.1.2", | ||
"codecov": "^3.2.0", | ||
@@ -45,4 +47,4 @@ "husky": "^1.3.1", | ||
"rimraf": "^2.6.3", | ||
"ts-jest": "^24.0.0", | ||
"ts-node": "^8.0.2", | ||
"ts-jest": "^24.0.0", | ||
"tslint": "^5.12.1", | ||
@@ -49,0 +51,0 @@ "tslint-config-prettier": "^1.18.0", |
167
README.md
@@ -1,31 +0,56 @@ | ||
## TeleportHQ Code Generators 0.3 - Alpha! | ||
<p align="center"> | ||
<img src="https://raw.githubusercontent.com/teleporthq/teleport-lib-js/master/logo50.png" width="250"/> | ||
</p> | ||
[![npm]()]() | ||
[](https://travis-ci.org/teleporthq/teleport-code-generators) | ||
[![Codecov]()]() | ||
[](https://github.com/prettier/prettier) | ||
<h2 align="center">Code Generators v0.3 - Alpha!</h2> | ||
This is a **WIP prototype** containing all our project and component generators, as well as the UIDL schemas and validators. While we have some working examples, it should not be considered production ready by any means! Do not hesitate to give us feedback and contribute back! | ||
<h3 align="center"> | ||
<a href="#what">What</a> | ||
<span> · </span> | ||
<a href="#quick-setup">Quick Setup</a> | ||
<span> · </span> | ||
<a href="#features">Features</a> | ||
<span> · </span> | ||
<a href="#development">Development</a> | ||
<span> · </span> | ||
<a href="#planning">Planning</a> | ||
<span> · </span> | ||
<a href="#contributions">Contributions</a> | ||
</h3> | ||
## What is this? | ||
The **code generators** are a part of the **TeleportHQ** ecosystem, which we are actively building, in an effort to streamline website and design generation. You can read more about our inception in [this article](https://teleporthq.io/blog/we-believe-in-AI-powered-code-generation/). | ||
<p align="center"> | ||
<a target="_blank" href="https://www.npmjs.com/package/@teleporthq/teleport-code-generators"><img src="https://img.shields.io/npm/v/@teleporthq/teleport-code-generators.svg" /></a> | ||
<a target="_blank" href="https://travis-ci.org/teleporthq/teleport-code-generators"><img src="https://travis-ci.org/teleporthq/teleport-code-generators.svg?branch=master" /></a> | ||
<a target="_blank" href="https://codecov.io/gh/teleporthq/teleport-code-generators"><img src="https://codecov.io/gh/teleporthq/teleport-code-generators/branch/master/graph/badge.svg" /></a> | ||
<a target="_blank" href="https://github.com/prettier/prettier"><img src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg" /></a> | ||
<img src="https://img.shields.io/npm/l/@teleporthq/teleport-code-generators.svg" /> | ||
<a target="_blank" href="https://bundlephobia.com/result?p=@teleporthq/teleport-code-generators"><img src="https://img.shields.io/bundlephobia/minzip/@teleporthq/teleport-code-generators.svg" /></a> | ||
<a target="_blank" href="https://twitter.com/teleporthqio"><img src="https://img.shields.io/twitter/follow/teleporthqio.svg" /></a> | ||
</p> | ||
The code generators are used by our online visual editor (coming soon), a tool which lets you build websites via a familiar design tool interface. The glue between our different tools and systems and the code generators is the [UIDL Standard](todo: blog link). The **UIDL** allows us to define **user interfaces** in an **abstract** way, independent of any framework or even the web platform itself, which then allows us to convert that abstraction into different flavors of coding (ex: React, Vue, etc.) | ||
This is a **WIP prototype** containing all of our project and component generators, as well as the **UIDL** schemas and validators. While we have some working examples, it shouldn't be considered production ready by any means! Don't hesitate to give us feedback and feel free to contribute in any way! | ||
<h2 id="what">🤔 What is this?</h2> | ||
The **code generators** are a part of the **teleportHQ** ecosystem, which we're actively building in an effort to streamline the creation of web and mobile applications. You can read more about our inception in [this article](https://teleporthq.io/blog/we-believe-in-AI-powered-code-generation/). | ||
The code generators are used by our online **visual editor** (coming soon), a platform that lets you build applications via a familiar design tool interface. The glue between our platform and the code generators is the [**UIDL Standard**](link coming soon). The UIDL allows us to define **user interfaces** in an **abstract** way, independent of any framework or even the web platform itself, which then allows us to convert that abstraction into different flavors of coding (e.g. React, Vue, etc.). | ||
Our philosophy behind the code generators is: | ||
* User interfaces are decomposed into **components**, hence our focus on component generation | ||
* What can be built with `React`, can also be built with `Vue` or on top of the `Web Components` standard, we support multiple targets | ||
* What can be built with `React`, can also be built with `Vue` or on top of the `Web Components` standard - we support multiple targets | ||
* A project built with the visual editor should have a **high standard of quality** (performance, security, accessibility included) | ||
* Generated **code quality** should be as good as possible, so that any developer can pick up the work from there and enhance the project | ||
* Generated **code quality** should be as high as possible, so that any developer could pick up the work from there on and enhance the project | ||
* The code generation architecture is open and extendable, we invite everyone to contribute! | ||
You can also read more about our [decision to open source our code generators](todo: blog link). | ||
You can also read more on our [decision to open source our code generators](link coming soon). | ||
Read more about the [UIDL Standard](todo: link). | ||
Read more about the [UIDL Standard](link coming soon). | ||
## Instalation and Basic Usage | ||
While this will probably remain a [monorepo](https://danluu.com/monorepo/), we will publish different **npm** packages for various parts of our code generation ecosystem. For now, there's a single package published under `@teleporthq/teleport-generators`. So, let's integrate that into your project: | ||
<h2 id="quick-setup">🚀 Quick Setup</h2> | ||
While this will probably remain a [monorepo](https://danluu.com/monorepo/), we'll publish different **npm** packages for various parts of our code generation ecosystem. For now, there's a single package published under `@teleporthq/teleport-generators`. So, let's integrate that into your project: | ||
```bash | ||
npm install @teleporthq/teleport-generators | ||
npm install @teleporthq/teleport-code-generators | ||
``` | ||
@@ -51,3 +76,3 @@ | ||
The code output from this snippet would be | ||
``` | ||
```jsx | ||
import React from "react" | ||
@@ -62,5 +87,9 @@ | ||
<h2 id="features">💼 Features</h2> | ||
This repo contains multiple **modules** that will soon be available as individual `npm` **packages**. There are two types of generators available: component and project generators. Component generators take a simple **ComponentUIDL** input and return the **code** according to the specific generator flavors (e.g. React + StyledJSX, Vue, etc.). Project generators operate on **ProjectUIDL**s and will return a complete structure of `folders` and `files` which then can be written to disk or sent to servers for deployment. The aim of the project generators is to output a **working application**. | ||
### Component Generators | ||
We have **official** component generators for `React` and `Vue`, but we plan on supporting other frameworks and standards as soon as possible. Also, `React Native` is definitely on our minds, because we designed the UIDL in such a way that it is agnostic of the web platform. | ||
We have **official** component generators for `React` and `Vue`, but we also plan on supporting other frameworks and standards as soon as possible. Also, `React Native` is definitely on our minds, since we've designed the UIDL in such a way that it's agnostic of the web platform. | ||
@@ -75,3 +104,3 @@ There are two **factory functions** exported from our main module, for the React and Vue generators. | ||
// Instantiate a generator, selecting the styled-jsx plugin for handling styles (other options: CSSModules, JSS, InlineStyles) | ||
const reactGenerator = createReactComponentGenerator({ variation: ReactComponentFlavors.StyledJSX }) | ||
const reactGenerator = createReactComponentGenerator({ variation: "StyledJSX" }) | ||
@@ -84,8 +113,6 @@ // Calling the generate function will return the code as a string | ||
Read more about [the API of the component generator](todo: doc link). | ||
Read more about [the API of the component generator](link coming soon). | ||
Read more about [mappings and resolvers](todo: doc link). | ||
Read more about [mappings and resolvers](link coming soon). | ||
TODO: More complex examples | ||
#### Vue | ||
@@ -105,8 +132,7 @@ | ||
TODO: More complex examples | ||
#### Current capabilities | ||
Here's a list of functionalities that the UIDL and the component generators support at the moment, besides the obvious presentational layer: | ||
#### Advanced capabilities | ||
Here's a list of functionalities that the UIDL and the component generators are supporting at the moment, besides the obvious presentational layer: | ||
* Dynamic values (props, state) inside html nodes or at attribute level | ||
* Type definitions for component props (PropTypes in React, props in Vue) | ||
* External dependencies definition | ||
* Simple component state (Hooks in React) | ||
@@ -118,5 +144,5 @@ * Event Handlers (related to state changes) | ||
We have **official** project generators for the two different frameworks we support so far. For `React`, we can generate a project based on a `React` and `React-Router` template, or we can generate a project with `Next.js`. For `Vue`, we have a standard `Vue` app, build with the `vue-cli` and a generator for `Nuxt`. | ||
We have **official** project generators for the two different frameworks we're supporting so far. For `React`, we can generate a project based on a `React` and `React-Router` template, or we can generate a project on top of `Next.js`. For `Vue`, we have a standard `Vue` app, build with the `vue-cli` and a generator for `Nuxt`. | ||
Project generators rely on the component generators and on the structure of the `ProjectUIDL` to figure out how many files to create and where. Each project generator has its own strategy, based on the particularities of that specific framework/tool. | ||
Project generators rely on the component generators and on the structure of the `ProjectUIDL` to figure out how many files to create and where to create them. Each project generator has its own strategy, based on the particularities of that specific framework/tool. | ||
@@ -139,5 +165,11 @@ #### React + React-Router | ||
#### Advanced Capabilities | ||
Besides the regular files and folders generated at the end of the process, project generators are also taking care of: | ||
* Extracting all external dependencies and adding them to the `package.json` | ||
* Creating the entry point for each application (it can be an `index.html` or something that is framework specific) | ||
* Generating a web manifest for PWA support | ||
### UIDL Validators | ||
The package also exports a module that performs UIDL validations for any given JSON structure, based on the [JSON Schema](todo: link documentation). | ||
The package also exports a module that performs UIDL validations for any given JSON structure, based on the [JSON Schema](link coming soon). | ||
@@ -156,29 +188,76 @@ ```javascript | ||
### Further Reading | ||
A few useful links to get you up to speed with the **teleport** ecosystem: | ||
* [Component](todo: link) and [Project](todo: link) JSON Schemas | ||
* [Full Documentation](https://teleporthq.io/docs) | ||
* [Introducing the new Generators](todo: link) | ||
A few useful links to get you up to speed with the entire **teleport** ecosystem: | ||
* [Component](link coming soon) and [Project](link coming soon) JSON Schemas | ||
* [Full Documentation](link coming soon) | ||
* [Introducing the new Generators](link coming soon) | ||
* [Playground link](link coming soon) | ||
## Development | ||
<h2 id="development">💻 Development</h2> | ||
This project is writen with **TypeScript**. The project setup is pretty standard. In order to give it a spin locally, you have to do: | ||
This project is writen with **TypeScript** and has a pretty standard setup. In order to give it a spin locally, you have to: | ||
``` | ||
npm install | ||
``` | ||
Project generators are running locally in the `/examples` folder, where you'll find a number of UIDL samples as well as the code that writes the files and folders to disk. | ||
To generate the projects locally, you can try one of these four examples: | ||
``` | ||
npm run create-react-basic | ||
npm run create-react-next | ||
npm run create-vue-basic | ||
npm run create-vue-nuxt | ||
``` | ||
Project generators are running locally in the `/examples` folder, where you will find a number of UIDL samples as well as the code that write the files and folders to disk. | ||
Files and folders for each template are generated after you run the corresponding npm task in `/examples/projects-exporters/<project-template>`. | ||
todo: Write about tests | ||
Running the test suite: | ||
``` | ||
npm run test | ||
npm run test:coverage | ||
``` | ||
## Planning | ||
todo: write about next steps | ||
Please [open an issue](https://github.com/teleporthq/teleport-code-generators/issues) for any irregularity, potential bug that you find while running the codebase, or if you simply have any questions or curiosities about this project. | ||
## Contributions | ||
We would be super happy to have community involvment around this project. We strongly believe in the power of open source, so we want to build the best possible code generators together with the entire development community. | ||
<h2 id="planning">🤖 Planning</h2> | ||
todo: Contact links | ||
It's not just our code that's open source, we're also planning the development of the code generators on GitHub. We already have [a number of issues](https://github.com/teleporthq/teleport-code-generators/issues) opened and we expect further contributions on this. | ||
We're especially interested in opening discussions around the issues tagged with the [`proposal`](https://github.com/teleporthq/teleport-code-generators/issues?q=is%3Aissue+is%3Aopen+label%3Aproposal) label. | ||
We also have a couple of milestone down the line: | ||
### Beta Release | ||
This is our immediately planned release for both the teleportHQ platform, as well as for the new generators and website. ETA for this release is mid April 2019, but this also depends on the release of other parts of the ecosystem. | ||
### Official Release | ||
Our official release will be a switch to version `1.0`. ETA for this is around May/June 2019. Hopefully, by then, we'll have more people contributing to the code generators. | ||
<h2 id="contributions">💕 Contributions</h2> | ||
We'd be super happy to have **community** involvement around this project. We strongly believe in the power of **open source**, so we're planning on building the best possible code generators, together with the entire development community. | ||
We envision different types of involvement from this point on: | ||
* Trying out the generators and reporting back any bugs and potential points of improvement | ||
* Contributing to the existing issues, either on the core modules or on the existing generators and plugins | ||
* Exploring and building new plugins for the existing generators | ||
* Exploring and building new generators based on the existing architecture | ||
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): | ||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --> | ||
<!-- prettier-ignore --> | ||
<table><tr><td align="center"><a href="https://medium.com/@alexnm"><img src="https://avatars0.githubusercontent.com/u/9945366?v=4" width="100px;" alt="Alex Moldovan"/><br /><sub><b>Alex Moldovan</b></sub></a><br /><a href="https://github.com/teleporthq/teleport-code-generators/commits?author=alexnm" title="Code">💻</a> <a href="https://github.com/teleporthq/teleport-code-generators/commits?author=alexnm" title="Documentation">📖</a> <a href="#ideas-alexnm" title="Ideas, Planning, & Feedback">🤔</a></td><td align="center"><a href="https://github.com/vladnicula"><img src="https://avatars3.githubusercontent.com/u/126038?v=4" width="100px;" alt="Vlad Nicula"/><br /><sub><b>Vlad Nicula</b></sub></a><br /><a href="https://github.com/teleporthq/teleport-code-generators/commits?author=vladnicula" title="Code">💻</a> <a href="#ideas-vladnicula" title="Ideas, Planning, & Feedback">🤔</a></td><td align="center"><a href="https://github.com/paulbrie"><img src="https://avatars2.githubusercontent.com/u/3997538?v=4" width="100px;" alt="Paul BRIE"/><br /><sub><b>Paul BRIE</b></sub></a><br /><a href="https://github.com/teleporthq/teleport-code-generators/issues?q=author%3Apaulbrie" title="Bug reports">🐛</a> <a href="https://github.com/teleporthq/teleport-code-generators/commits?author=paulbrie" title="Documentation">📖</a> <a href="#ideas-paulbrie" title="Ideas, Planning, & Feedback">🤔</a></td><td align="center"><a href="https://github.com/mihaitaba"><img src="https://avatars3.githubusercontent.com/u/45386599?v=4" width="100px;" alt="mihaitaba"/><br /><sub><b>mihaitaba</b></sub></a><br /><a href="#design-mihaitaba" title="Design">🎨</a> <a href="https://github.com/teleporthq/teleport-code-generators/commits?author=mihaitaba" title="Documentation">📖</a></td></tr></table> | ||
<!-- ALL-CONTRIBUTORS-LIST:END --> | ||
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! | ||
<h2 id="contact">✍️ Contact</h2> | ||
Reach out to us on any of these channels: | ||
* 📧 [Write an Email](mailto:hello@teleporthq.io) | ||
* 🐦 [Drop a message on twitter](https://twitter.com/teleporthqio) | ||
* ℹ️ [Website](https://teleporthq.io/) |
@@ -1,6 +0,6 @@ | ||
import { ComponentPlugin, ComponentStructure } from '../../types' | ||
import { ComponentPlugin, ComponentStructure, ChunkDefinition } from '../../types' | ||
import { ComponentUIDL } from '../../../uidl-definitions/types' | ||
export default class ComponentAssemblyLine { | ||
export default class AssemblyLine { | ||
private plugins: ComponentPlugin[] | ||
@@ -40,2 +40,13 @@ | ||
} | ||
public groupChunksByFileId(chunks: ChunkDefinition[]): Record<string, ChunkDefinition[]> { | ||
return chunks.reduce((chunksByFileId: Record<string, ChunkDefinition[]>, chunk) => { | ||
const fileId = (chunk.meta && chunk.meta.fileId) || 'default' | ||
if (!chunksByFileId[fileId]) { | ||
chunksByFileId[fileId] = [] | ||
} | ||
chunksByFileId[fileId].push(chunk) | ||
return chunksByFileId | ||
}, {}) | ||
} | ||
} |
@@ -6,23 +6,2 @@ import { ChunkDefinition, GeneratorFunction } from '../../types' | ||
const removeItemsInArray = (arrayToRemoveFrom: string[], itemsToRemove: string[]) => { | ||
return arrayToRemoveFrom.filter((item: string) => { | ||
return itemsToRemove.indexOf(item) === -1 | ||
}) | ||
} | ||
const removeChildDependency = (children: EmbedDependency[], targetChunkName: string) => { | ||
return children.reduce((acc: EmbedDependency[], child) => { | ||
if (child.chunkName !== targetChunkName) { | ||
acc.push(child) | ||
} | ||
return acc | ||
}, []) | ||
} | ||
interface EmbedDependency { | ||
chunkName: string | ||
slot: string | ||
} | ||
export default class Builder { | ||
@@ -252,1 +231,22 @@ public chunkDefinitions: ChunkDefinition[] = [] | ||
} | ||
const removeItemsInArray = (arrayToRemoveFrom: string[], itemsToRemove: string[]) => { | ||
return arrayToRemoveFrom.filter((item: string) => { | ||
return itemsToRemove.indexOf(item) === -1 | ||
}) | ||
} | ||
const removeChildDependency = (children: EmbedDependency[], targetChunkName: string) => { | ||
return children.reduce((acc: EmbedDependency[], child) => { | ||
if (child.chunkName !== targetChunkName) { | ||
acc.push(child) | ||
} | ||
return acc | ||
}, []) | ||
} | ||
interface EmbedDependency { | ||
chunkName: string | ||
slot: string | ||
} |
@@ -1,3 +0,3 @@ | ||
export { default as ComponentAssemblyLine } from './assembly-line' | ||
export { default as AssemblyLine } from './assembly-line' | ||
export { default as Builder } from './builder' | ||
export { default as Resolver } from './resolver' |
@@ -9,5 +9,118 @@ import { | ||
import { prefixPlaygroundAssetsURL } from '../../utils/uidl-utils' | ||
import { ASSETS_IDENTIFIER } from '../../constants' | ||
const STYLE_PROPERTIES_WITH_URL = ['background', 'backgroundImage'] | ||
const ATTRIBUTES_WITH_URL = ['url', 'srcset'] | ||
export const resolveContentNode = ( | ||
node: ContentNode, | ||
elementsMapping: ElementsMapping, | ||
localDependenciesPrefix: string, | ||
assetsPrefix?: string | ||
) => { | ||
const mappedElement = elementsMapping[node.type] || { type: node.type } | ||
node.type = mappedElement.type | ||
// If the mapping contains children, insert that structure into the UIDL | ||
if (mappedElement.children) { | ||
const originalNodeChildren = node.children || [] | ||
const originalAttrs = node.attrs || {} | ||
const replacingNode = { | ||
...node, | ||
children: JSON.parse(JSON.stringify(mappedElement.children)), | ||
} | ||
insertChildrenIntoNode(replacingNode, originalNodeChildren, originalAttrs) | ||
node.children = replacingNode.children | ||
} | ||
// Resolve dependency with the UIDL having priority | ||
if (node.dependency || mappedElement.dependency) { | ||
node.dependency = resolveDependency(mappedElement, node.dependency, localDependenciesPrefix) | ||
} | ||
// Resolve assets prefix inside style (ex: background-image) | ||
if (node.styles && assetsPrefix) { | ||
node.styles = prefixAssetURLs(node.styles, assetsPrefix) | ||
} | ||
// Prefix the attributes which may point to local assets | ||
if (node.attrs && assetsPrefix) { | ||
ATTRIBUTES_WITH_URL.forEach((attribute) => { | ||
if (node.attrs[attribute]) { | ||
node.attrs[attribute] = prefixPlaygroundAssetsURL(assetsPrefix, node.attrs[attribute]) | ||
} | ||
}) | ||
} | ||
// Merge UIDL attributes to the attributes coming from the mapping object | ||
if (mappedElement.attrs) { | ||
node.attrs = mergeAttributes(mappedElement, node.attrs) | ||
} | ||
// The UIDL has priority over the mapping repeat | ||
const repeatStructure = node.repeat || mappedElement.repeat | ||
if (repeatStructure) { | ||
const { dataSource, content } = repeatStructure | ||
// Data source might be preset on a referenced attribute in the uidl node | ||
// ex: attrs[options] in case of a dropdown primitive with select/options | ||
if (typeof dataSource === 'string' && dataSource.startsWith('$attrs.') && node.attrs) { | ||
const nodeDataSourceAttr = dataSource.replace('$attrs.', '') | ||
repeatStructure.dataSource = node.attrs[nodeDataSourceAttr] | ||
} | ||
// The content inside the repeat must also be mapped like any regular content node | ||
repeatStructure.content = resolveContentNode( | ||
content, | ||
elementsMapping, | ||
localDependenciesPrefix, | ||
assetsPrefix | ||
) | ||
node.repeat = repeatStructure | ||
} | ||
// If the node has multiple state branches, each content needs to be resolved | ||
if (node.type === 'state' && node.states) { | ||
node.states = node.states.map((stateBranch) => { | ||
if (typeof stateBranch.content === 'string') { | ||
return stateBranch | ||
} else { | ||
return { | ||
...stateBranch, | ||
content: resolveContentNode( | ||
stateBranch.content, | ||
elementsMapping, | ||
localDependenciesPrefix, | ||
assetsPrefix | ||
), | ||
} | ||
} | ||
}) | ||
} | ||
// Traverse the UIDL | ||
if (node.children) { | ||
node.children = node.children.map((child) => { | ||
if (typeof child === 'string') { | ||
return child | ||
} else { | ||
return resolveContentNode(child, elementsMapping, localDependenciesPrefix, assetsPrefix) | ||
} | ||
}) | ||
} | ||
return node | ||
} | ||
/** | ||
* Prefixes all urls inside the style object with the assetsPrefix | ||
* @param styles the style object on the current node | ||
* @param assetsPrefix a string representing the asset prefix | ||
*/ | ||
const prefixAssetURLs = (styles: StyleDefinitions, assetsPrefix: string): StyleDefinitions => { | ||
const whitelistStyleProperties = ['background', 'backgroundImage'] | ||
// iterate through all the style keys | ||
@@ -30,8 +143,8 @@ return Object.keys(styles).reduce((acc, styleKey) => { | ||
// only whitelisted style properties are checked | ||
if (whitelistStyleProperties.includes(styleKey) && styleValue.includes('url("')) { | ||
// position index after the double quotes to introduce the url prefix before | ||
const startIndex = styleValue.indexOf('url("') + 5 | ||
if (STYLE_PROPERTIES_WITH_URL.includes(styleKey) && styleValue.includes(ASSETS_IDENTIFIER)) { | ||
// split the string at the beginning of the ASSETS_IDENTIFIER string | ||
const startIndex = styleValue.indexOf(ASSETS_IDENTIFIER) | ||
acc[styleKey] = | ||
styleValue.slice(0, startIndex) + | ||
prefixRelativeURL(assetsPrefix, styleValue.slice(startIndex, styleValue.length - 1)) | ||
prefixPlaygroundAssetsURL(assetsPrefix, styleValue.slice(startIndex, styleValue.length)) | ||
} else { | ||
@@ -45,20 +158,3 @@ acc[styleKey] = styleValue | ||
/** | ||
* Concatenates the prefix to the URL string. | ||
* If the url starts with 'http', the return value will be the 'originalString' | ||
* If the url does not start with a '/' it also appends that | ||
*/ | ||
const prefixRelativeURL = (prefix: string, originalString: string) => { | ||
if (originalString.startsWith('http')) { | ||
return originalString | ||
} | ||
if (originalString.startsWith('/')) { | ||
return prefix + originalString | ||
} | ||
return `${prefix}/${originalString}` | ||
} | ||
const mergeAttributes = (mappedElement: ElementMapping, uidlAttrs: any, assetsPrefix?: string) => { | ||
const mergeAttributes = (mappedElement: ElementMapping, uidlAttrs: any) => { | ||
// We gather the results here uniting the mapped attributes and the uidl attributes. | ||
@@ -85,7 +181,3 @@ const resolvedAttrs: Record<string, any> = {} | ||
if (uidlAttrs && uidlAttrs[uidlAttributeKey]) { | ||
resolvedAttrs[key] = | ||
key === 'src' && assetsPrefix | ||
? prefixRelativeURL(assetsPrefix, uidlAttrs[uidlAttributeKey]) | ||
: uidlAttrs[uidlAttributeKey] | ||
resolvedAttrs[key] = uidlAttrs[uidlAttributeKey] | ||
mappedAttributes.push(uidlAttributeKey) | ||
@@ -133,4 +225,19 @@ } | ||
node: ContentNode, | ||
originalChildren: Array<ContentNode | string> | ||
originalChildren: Array<ContentNode | string>, | ||
originalAttrs: Record<string, any> | ||
) => { | ||
// The same kind of referencing that is done in the mergeAttributes function | ||
// TODO: Extract duplicate code and apply in both instances (merge attributes and solving children nodes) | ||
// Explained here: https://github.com/teleporthq/teleport-code-generators/issues/44 | ||
// Object.keys(node.attrs).forEach((attrKey) => { | ||
// if (typeof node.attrs[attrKey] === 'string' && node.attrs[attrKey].startsWith('$attrs.')) { | ||
// const referencedAttributeKey = node.attrs[attrKey].replace('$attrs.', '') | ||
// if (originalAttrs[referencedAttributeKey]) { | ||
// node.attrs[attrKey] = originalAttrs[referencedAttributeKey] | ||
// // since the attribute is mapped in the children, we assume it is not longer needed on the root node | ||
// delete originalAttrs[referencedAttributeKey] | ||
// } | ||
// } | ||
// }) | ||
if (!node.children) { | ||
@@ -155,3 +262,3 @@ return | ||
// The child node is pushed after the $children token was replaced | ||
insertChildrenIntoNode(child, originalChildren) | ||
insertChildrenIntoNode(child, originalChildren, originalAttrs) | ||
acc.push(child) | ||
@@ -161,95 +268,1 @@ return acc | ||
} | ||
export const resolveContentNode = ( | ||
node: ContentNode, | ||
elementsMapping: ElementsMapping, | ||
localDependenciesPrefix: string, | ||
assetsPrefix?: string | ||
) => { | ||
const mappedElement = elementsMapping[node.type] || { type: node.type } | ||
node.type = mappedElement.type | ||
// Resolve dependency with the UIDL having priority | ||
if (node.dependency || mappedElement.dependency) { | ||
node.dependency = resolveDependency(mappedElement, node.dependency, localDependenciesPrefix) | ||
} | ||
// Resolve assets prefix inside style (ex: background-image) | ||
if (node.styles && assetsPrefix) { | ||
node.styles = prefixAssetURLs(node.styles, assetsPrefix) | ||
} | ||
// Merge UIDL attributes to the attributes coming from the mapping object | ||
if (mappedElement.attrs) { | ||
node.attrs = mergeAttributes(mappedElement, node.attrs, assetsPrefix) | ||
} | ||
// If the mapping contains children, insert that structure into the UIDL | ||
if (mappedElement.children) { | ||
const originalNodeChildren = node.children || [] | ||
const replacingNode = { | ||
...node, | ||
children: JSON.parse(JSON.stringify(mappedElement.children)), | ||
} | ||
insertChildrenIntoNode(replacingNode, originalNodeChildren) | ||
node.children = replacingNode.children | ||
} | ||
// The UIDL has priority over the mapping repeat | ||
const repeatStructure = node.repeat || mappedElement.repeat | ||
if (repeatStructure) { | ||
const { dataSource, content } = repeatStructure | ||
// Data source might be preset on a referenced attribute in the uidl node | ||
// ex: attrs[options] in case of a dropdown primitive with select/options | ||
if (typeof dataSource === 'string' && dataSource.startsWith('$attrs.') && node.attrs) { | ||
const nodeDataSourceAttr = dataSource.replace('$attrs.', '') | ||
repeatStructure.dataSource = node.attrs[nodeDataSourceAttr] | ||
} | ||
// The content inside the repeat must also be mapped like any regular content node | ||
repeatStructure.content = resolveContentNode( | ||
content, | ||
elementsMapping, | ||
localDependenciesPrefix, | ||
assetsPrefix | ||
) | ||
node.repeat = repeatStructure | ||
} | ||
// If the node has multiple state branches, each content needs to be resolved | ||
if (node.type === 'state' && node.states) { | ||
node.states = node.states.map((stateBranch) => { | ||
if (typeof stateBranch.content === 'string') { | ||
return stateBranch | ||
} else { | ||
return { | ||
...stateBranch, | ||
content: resolveContentNode( | ||
stateBranch.content, | ||
elementsMapping, | ||
localDependenciesPrefix, | ||
assetsPrefix | ||
), | ||
} | ||
} | ||
}) | ||
} | ||
// Traverse the UIDL | ||
if (node.children) { | ||
node.children = node.children.map((child) => { | ||
if (typeof child === 'string') { | ||
return child | ||
} else { | ||
return resolveContentNode(child, elementsMapping, localDependenciesPrefix, assetsPrefix) | ||
} | ||
}) | ||
} | ||
return node | ||
} |
@@ -6,2 +6,33 @@ import { ComponentPlugin, ComponentPluginFactory, ChunkDefinition } from '../../types' | ||
interface ImportPluginConfig { | ||
importLibsChunkName?: string | ||
importPackagesChunkName?: string | ||
importLocalsChunkName?: string | ||
} | ||
export const createPlugin: ComponentPluginFactory<ImportPluginConfig> = (config) => { | ||
const { | ||
importLibsChunkName = 'import-libs', | ||
importPackagesChunkName = 'import-packages', | ||
importLocalsChunkName = 'import-local', | ||
fileId = null, | ||
} = config || {} | ||
const importPlugin: ComponentPlugin = async (structure) => { | ||
const { dependencies } = structure | ||
const libraryDependencies = groupDependenciesByPackage(dependencies, 'library') | ||
const packageDependencies = groupDependenciesByPackage(dependencies, 'package') | ||
const localDependencies = groupDependenciesByPackage(dependencies, 'local') | ||
addImportChunk(structure.chunks, libraryDependencies, importLibsChunkName, fileId) | ||
addImportChunk(structure.chunks, packageDependencies, importPackagesChunkName, fileId) | ||
addImportChunk(structure.chunks, localDependencies, importLocalsChunkName, fileId) | ||
return structure | ||
} | ||
return importPlugin | ||
} | ||
export default createPlugin() | ||
interface ImportDependency { | ||
@@ -65,32 +96,1 @@ identifier: string | ||
} | ||
interface ImportPluginConfig { | ||
importLibsChunkName?: string | ||
importPackagesChunkName?: string | ||
importLocalsChunkName?: string | ||
} | ||
export const createPlugin: ComponentPluginFactory<ImportPluginConfig> = (config) => { | ||
const { | ||
importLibsChunkName = 'import-libs', | ||
importPackagesChunkName = 'import-packages', | ||
importLocalsChunkName = 'import-local', | ||
fileId = null, | ||
} = config || {} | ||
const importPlugin: ComponentPlugin = async (structure) => { | ||
const { dependencies } = structure | ||
const libraryDependencies = groupDependenciesByPackage(dependencies, 'library') | ||
const packageDependencies = groupDependenciesByPackage(dependencies, 'package') | ||
const localDependencies = groupDependenciesByPackage(dependencies, 'local') | ||
addImportChunk(structure.chunks, libraryDependencies, importLibsChunkName, fileId) | ||
addImportChunk(structure.chunks, packageDependencies, importPackagesChunkName, fileId) | ||
addImportChunk(structure.chunks, localDependencies, importLocalsChunkName, fileId) | ||
return structure | ||
} | ||
return importPlugin | ||
} | ||
export default createPlugin() |
@@ -34,5 +34,3 @@ import * as t from '@babel/types' | ||
if (typeof pageKey !== 'string' || typeof content === 'string') { | ||
console.warn( | ||
'Route not correctly specified. Value should be a string when defining routes' | ||
) | ||
console.warn('Route not correctly specified. Value should be a string when defining routes') | ||
return null | ||
@@ -39,0 +37,0 @@ } |
@@ -8,3 +8,3 @@ import * as t from '@babel/types' | ||
path: 'react', | ||
version: '16.6.3', | ||
version: '16.8.3', | ||
} | ||
@@ -15,3 +15,3 @@ | ||
path: 'react-dom', | ||
version: '16.6.3', | ||
version: '16.8.3', | ||
} | ||
@@ -18,0 +18,0 @@ |
@@ -5,7 +5,3 @@ import * as t from '@babel/types' | ||
addChildJSXTag, | ||
addChildJSXText, | ||
addAttributeToJSXTag, | ||
generateASTDefinitionForJSXTag, | ||
addDynamicChild, | ||
addDynamicPropOnJsxOpeningTag, | ||
createConditionalJSXExpression, | ||
@@ -15,3 +11,9 @@ } from '../../../utils/jsx-ast' | ||
import { makeDefaultExport } from '../../../utils/js-ast' | ||
import { addEventHandlerToTag, makePureComponent, makeRepeatStructureWithMap } from './utils' | ||
import { | ||
addEventHandlerToTag, | ||
makePureComponent, | ||
makeRepeatStructureWithMap, | ||
addAttributeToTag, | ||
addTextElementToTag, | ||
} from './utils' | ||
@@ -28,40 +30,95 @@ import { capitalize } from '../../../utils/helpers' | ||
/** | ||
* | ||
* @param tag the ref to the AST tag under construction | ||
* @param key the key of the attribute that should be added on the current AST node | ||
* @param value the value(string, number, bool) of the attribute that should be added on the current AST node | ||
*/ | ||
const addAttributeToTag = (tag: t.JSXElement, key: string, value: any) => { | ||
if (typeof value !== 'string') { | ||
addAttributeToJSXTag(tag, { name: key, value }) | ||
return | ||
} | ||
interface JSXConfig { | ||
componentChunkName: string | ||
exportChunkName: string | ||
importChunkName: string | ||
} | ||
if (value.startsWith('$props.')) { | ||
const dynamicPropValue = value.replace('$props.', '') | ||
addDynamicPropOnJsxOpeningTag(tag, key, dynamicPropValue, 'props') | ||
} else if (value.startsWith('$state.')) { | ||
const dynamicPropValue = value.replace('$state.', '') | ||
addDynamicPropOnJsxOpeningTag(tag, key, dynamicPropValue) | ||
} else if (value === '$item' || value === '$index') { | ||
addDynamicPropOnJsxOpeningTag(tag, key, value.slice(1)) | ||
} else { | ||
addAttributeToJSXTag(tag, { name: key, value }) | ||
export const createPlugin: ComponentPluginFactory<JSXConfig> = (config) => { | ||
const { | ||
componentChunkName = 'react-component', | ||
exportChunkName = 'export', | ||
importChunkName = 'import', | ||
} = config || {} | ||
const reactComponentPlugin: ComponentPlugin = async (structure) => { | ||
const { uidl, dependencies } = structure | ||
dependencies.React = { | ||
type: 'library', | ||
path: 'react', | ||
version: '16.8.3', | ||
} | ||
let stateIdentifiers: Record<string, StateIdentifier> = {} | ||
if (uidl.stateDefinitions) { | ||
dependencies.useState = { | ||
type: 'library', | ||
path: 'react', | ||
version: '16.8.3', | ||
meta: { | ||
namedImport: true, | ||
}, | ||
} | ||
const stateDefinitions = uidl.stateDefinitions | ||
stateIdentifiers = Object.keys(stateDefinitions).reduce( | ||
(acc: Record<string, StateIdentifier>, stateKey: string) => { | ||
acc[stateKey] = { | ||
key: stateKey, | ||
type: stateDefinitions[stateKey].type, | ||
default: stateDefinitions[stateKey].defaultValue, | ||
setter: 'set' + capitalize(stateKey), | ||
} | ||
return acc | ||
}, | ||
{} | ||
) | ||
} | ||
// We will keep a flat mapping object from each component identifier (from the UIDL) to its correspoding JSX AST Tag | ||
// This will help us inject style or classes at a later stage in the pipeline, upon traversing the UIDL | ||
// The structure will be populated as the AST is being created | ||
const nodesLookup = {} | ||
const jsxTagStructure = generateTreeStructure( | ||
uidl.content, | ||
uidl.propDefinitions || {}, | ||
stateIdentifiers, | ||
nodesLookup, | ||
dependencies | ||
) | ||
const pureComponent = makePureComponent(uidl.name, stateIdentifiers, jsxTagStructure) | ||
structure.chunks.push({ | ||
type: 'js', | ||
name: componentChunkName, | ||
linker: { | ||
after: [importChunkName], | ||
}, | ||
meta: { | ||
nodesLookup, | ||
}, | ||
content: pureComponent, | ||
}) | ||
structure.chunks.push({ | ||
type: 'js', | ||
name: exportChunkName, | ||
linker: { | ||
after: [componentChunkName], | ||
}, | ||
content: makeDefaultExport(uidl.name), | ||
}) | ||
return structure | ||
} | ||
} | ||
const addTextElementToTag = (tag: t.JSXElement, text: string) => { | ||
if (text.startsWith('$props.') && !text.endsWith('$props.')) { | ||
addDynamicChild(tag, text.replace('$props.', ''), 'props') | ||
} else if (text.startsWith('$state.') && !text.endsWith('$state.')) { | ||
addDynamicChild(tag, text.replace('$state.', '')) | ||
} else if (text === '$item' || text === '$index') { | ||
addDynamicChild(tag, text.slice(1)) | ||
} else { | ||
addChildJSXText(tag, text) | ||
} | ||
return reactComponentPlugin | ||
} | ||
export const generateTreeStructure = ( | ||
export default createPlugin() | ||
const generateTreeStructure = ( | ||
content: ContentNode, | ||
@@ -176,91 +233,1 @@ propDefinitions: Record<string, PropDefinition>, | ||
} | ||
interface JSXConfig { | ||
componentChunkName: string | ||
exportChunkName: string | ||
importChunkName: string | ||
} | ||
export const createPlugin: ComponentPluginFactory<JSXConfig> = (config) => { | ||
const { | ||
componentChunkName = 'react-component', | ||
exportChunkName = 'export', | ||
importChunkName = 'import', | ||
} = config || {} | ||
const reactComponentPlugin: ComponentPlugin = async (structure) => { | ||
const { uidl, dependencies } = structure | ||
dependencies.React = { | ||
type: 'library', | ||
path: 'react', | ||
} | ||
let stateIdentifiers: Record<string, StateIdentifier> = {} | ||
if (uidl.stateDefinitions) { | ||
dependencies.useState = { | ||
type: 'library', | ||
path: 'react', | ||
meta: { | ||
namedImport: true, | ||
}, | ||
} | ||
const stateDefinitions = uidl.stateDefinitions | ||
stateIdentifiers = Object.keys(stateDefinitions).reduce( | ||
(acc: Record<string, StateIdentifier>, stateKey: string) => { | ||
acc[stateKey] = { | ||
key: stateKey, | ||
type: stateDefinitions[stateKey].type, | ||
default: stateDefinitions[stateKey].defaultValue, | ||
setter: 'set' + capitalize(stateKey), | ||
} | ||
return acc | ||
}, | ||
{} | ||
) | ||
} | ||
// We will keep a flat mapping object from each component identifier (from the UIDL) to its correspoding JSX AST Tag | ||
// This will help us inject style or classes at a later stage in the pipeline, upon traversing the UIDL | ||
// The structure will be populated as the AST is being created | ||
const nodesLookup = {} | ||
const jsxTagStructure = generateTreeStructure( | ||
uidl.content, | ||
uidl.propDefinitions || {}, | ||
stateIdentifiers, | ||
nodesLookup, | ||
dependencies | ||
) | ||
const pureComponent = makePureComponent(uidl.name, stateIdentifiers, jsxTagStructure) | ||
structure.chunks.push({ | ||
type: 'js', | ||
name: componentChunkName, | ||
linker: { | ||
after: [importChunkName], | ||
}, | ||
meta: { | ||
nodesLookup, | ||
}, | ||
content: pureComponent, | ||
}) | ||
structure.chunks.push({ | ||
type: 'js', | ||
name: exportChunkName, | ||
linker: { | ||
after: [componentChunkName], | ||
}, | ||
content: makeDefaultExport(uidl.name), | ||
}) | ||
return structure | ||
} | ||
return reactComponentPlugin | ||
} | ||
export default createPlugin() |
import * as types from '@babel/types' | ||
import { StateIdentifier } from '../../../types' | ||
import { convertValueToLiteral } from '../../../utils/js-ast' | ||
import { | ||
addChildJSXText, | ||
addAttributeToJSXTag, | ||
addDynamicChild, | ||
addDynamicPropOnJsxOpeningTag, | ||
} from '../../../utils/jsx-ast' | ||
import { EventHandlerStatement, PropDefinition } from '../../../../uidl-definitions/types' | ||
const createStateChangeStatement = ( | ||
eventHandlerStatement: EventHandlerStatement, | ||
// Adds all the event handlers and all the instructions for each event handler | ||
// in case there is more than one specified in the UIDL | ||
export const addEventHandlerToTag = ( | ||
tag: types.JSXElement, | ||
eventKey: string, | ||
eventHandlerStatements: EventHandlerStatement[], | ||
stateIdentifiers: Record<string, StateIdentifier>, | ||
propDefinitions: Record<string, PropDefinition> = {}, | ||
t = types | ||
) => { | ||
if (!eventHandlerStatement.modifies) { | ||
console.warn(`No state identifier referenced under the "modifies" field`) | ||
return null | ||
} | ||
const eventHandlerASTStatements: types.ExpressionStatement[] = [] | ||
const stateKey = eventHandlerStatement.modifies | ||
const stateIdentifier = stateIdentifiers[stateKey] | ||
eventHandlerStatements.forEach((eventHandlerAction) => { | ||
if (eventHandlerAction.type === 'stateChange') { | ||
const handler = createStateChangeStatement(eventHandlerAction, stateIdentifiers) | ||
if (handler) { | ||
eventHandlerASTStatements.push(handler) | ||
} | ||
} | ||
if (!stateIdentifier) { | ||
console.warn(`No state hook was found for "${stateKey}"`) | ||
return null | ||
if (eventHandlerAction.type === 'propCall') { | ||
const handler = createPropCallStatement(eventHandlerAction, propDefinitions) | ||
if (handler) { | ||
eventHandlerASTStatements.push(handler) | ||
} | ||
} | ||
}) | ||
let expressionContent: types.ArrowFunctionExpression | types.Expression | ||
if (eventHandlerASTStatements.length === 1) { | ||
const expression = eventHandlerASTStatements[0].expression | ||
expressionContent = | ||
expression.type === 'CallExpression' && expression.arguments.length === 0 | ||
? expression.callee | ||
: t.arrowFunctionExpression([], expression) | ||
} else { | ||
expressionContent = t.arrowFunctionExpression([], t.blockStatement(eventHandlerASTStatements)) | ||
} | ||
const stateSetterArgument = | ||
eventHandlerStatement.newState === '$toggle' | ||
? t.unaryExpression('!', t.identifier(stateIdentifier.key)) | ||
: convertValueToLiteral(eventHandlerStatement.newState, stateIdentifier.type) | ||
return t.expressionStatement( | ||
t.callExpression(t.identifier(stateIdentifier.setter), [stateSetterArgument]) | ||
tag.openingElement.attributes.push( | ||
t.jsxAttribute(t.jsxIdentifier(eventKey), t.jsxExpressionContainer(expressionContent)) | ||
) | ||
@@ -60,44 +83,27 @@ } | ||
// Adds all the event handlers and all the instructions for each event handler | ||
// in case there is more than one specified in the UIDL | ||
export const addEventHandlerToTag = ( | ||
tag: types.JSXElement, | ||
eventKey: string, | ||
eventHandlerStatements: EventHandlerStatement[], | ||
const createStateChangeStatement = ( | ||
eventHandlerStatement: EventHandlerStatement, | ||
stateIdentifiers: Record<string, StateIdentifier>, | ||
propDefinitions: Record<string, PropDefinition> = {}, | ||
t = types | ||
) => { | ||
const eventHandlerASTStatements: types.ExpressionStatement[] = [] | ||
if (!eventHandlerStatement.modifies) { | ||
console.warn(`No state identifier referenced under the "modifies" field`) | ||
return null | ||
} | ||
eventHandlerStatements.forEach((eventHandlerAction) => { | ||
if (eventHandlerAction.type === 'stateChange') { | ||
const handler = createStateChangeStatement(eventHandlerAction, stateIdentifiers) | ||
if (handler) { | ||
eventHandlerASTStatements.push(handler) | ||
} | ||
} | ||
const stateKey = eventHandlerStatement.modifies | ||
const stateIdentifier = stateIdentifiers[stateKey] | ||
if (eventHandlerAction.type === 'propCall') { | ||
const handler = createPropCallStatement(eventHandlerAction, propDefinitions) | ||
if (handler) { | ||
eventHandlerASTStatements.push(handler) | ||
} | ||
} | ||
}) | ||
if (!stateIdentifier) { | ||
console.warn(`No state hook was found for "${stateKey}"`) | ||
return null | ||
} | ||
let expressionContent: types.ArrowFunctionExpression | types.Expression | ||
if (eventHandlerASTStatements.length === 1) { | ||
const expression = eventHandlerASTStatements[0].expression | ||
const stateSetterArgument = | ||
eventHandlerStatement.newState === '$toggle' | ||
? t.unaryExpression('!', t.identifier(stateIdentifier.key)) | ||
: convertValueToLiteral(eventHandlerStatement.newState, stateIdentifier.type) | ||
expressionContent = | ||
expression.type === 'CallExpression' && expression.arguments.length === 0 | ||
? expression.callee | ||
: t.arrowFunctionExpression([], expression) | ||
} else { | ||
expressionContent = t.arrowFunctionExpression([], t.blockStatement(eventHandlerASTStatements)) | ||
} | ||
tag.openingElement.attributes.push( | ||
t.jsxAttribute(t.jsxIdentifier(eventKey), t.jsxExpressionContainer(expressionContent)) | ||
return t.expressionStatement( | ||
t.callExpression(t.identifier(stateIdentifier.setter), [stateSetterArgument]) | ||
) | ||
@@ -164,1 +170,38 @@ } | ||
} | ||
/** | ||
* | ||
* @param tag the ref to the AST tag under construction | ||
* @param key the key of the attribute that should be added on the current AST node | ||
* @param value the value(string, number, bool) of the attribute that should be added on the current AST node | ||
*/ | ||
export const addAttributeToTag = (tag: types.JSXElement, key: string, value: any) => { | ||
if (typeof value !== 'string') { | ||
addAttributeToJSXTag(tag, { name: key, value }) | ||
return | ||
} | ||
if (value.startsWith('$props.')) { | ||
const dynamicPropValue = value.replace('$props.', '') | ||
addDynamicPropOnJsxOpeningTag(tag, key, dynamicPropValue, 'props') | ||
} else if (value.startsWith('$state.')) { | ||
const dynamicPropValue = value.replace('$state.', '') | ||
addDynamicPropOnJsxOpeningTag(tag, key, dynamicPropValue) | ||
} else if (value === '$item' || value === '$index') { | ||
addDynamicPropOnJsxOpeningTag(tag, key, value.slice(1)) | ||
} else { | ||
addAttributeToJSXTag(tag, { name: key, value }) | ||
} | ||
} | ||
export const addTextElementToTag = (tag: types.JSXElement, text: string) => { | ||
if (text.startsWith('$props.') && !text.endsWith('$props.')) { | ||
addDynamicChild(tag, text.replace('$props.', ''), 'props') | ||
} else if (text.startsWith('$state.') && !text.endsWith('$state.')) { | ||
addDynamicChild(tag, text.replace('$state.', '')) | ||
} else if (text === '$item' || text === '$index') { | ||
addDynamicChild(tag, text.slice(1)) | ||
} else { | ||
addChildJSXText(tag, text) | ||
} | ||
} |
@@ -9,2 +9,31 @@ import { ComponentPlugin, ComponentPluginFactory } from '../../types' | ||
interface InlineStyleConfig { | ||
componentChunkName: string | ||
} | ||
export const createPlugin: ComponentPluginFactory<InlineStyleConfig> = (config) => { | ||
const { componentChunkName = 'react-component' } = config || {} | ||
/** | ||
* Generate the inlines stlye definition as a AST block which will represent the | ||
* defined styles of this component in UIDL | ||
* | ||
* @param structure : ComponentStructure | ||
*/ | ||
const reactInlineStyleComponentPlugin: ComponentPlugin = async (structure) => { | ||
const { uidl, chunks } = structure | ||
const componentChunk = chunks.find((chunk) => chunk.name === componentChunkName) | ||
if (!componentChunk) { | ||
return structure | ||
} | ||
enhanceJSXWithStyles(uidl.content, componentChunk.meta.nodesLookup) | ||
return structure | ||
} | ||
return reactInlineStyleComponentPlugin | ||
} | ||
export default createPlugin() | ||
const prepareDynamicProps = (styles: UIDLTypes.StyleDefinitions) => { | ||
@@ -60,30 +89,1 @@ return Object.keys(styles).reduce((acc: any, key) => { | ||
} | ||
interface InlineStyleConfig { | ||
componentChunkName: string | ||
} | ||
export const createPlugin: ComponentPluginFactory<InlineStyleConfig> = (config) => { | ||
const { componentChunkName = 'react-component' } = config || {} | ||
/** | ||
* Generate the inlines stlye definition as a AST block which will represent the | ||
* defined styles of this component in UIDL | ||
* | ||
* @param structure : ComponentStructure | ||
*/ | ||
const reactInlineStyleComponentPlugin: ComponentPlugin = async (structure) => { | ||
const { uidl, chunks } = structure | ||
const componentChunk = chunks.find((chunk) => chunk.name === componentChunkName) | ||
if (!componentChunk) { | ||
return structure | ||
} | ||
enhanceJSXWithStyles(uidl.content, componentChunk.meta.nodesLookup) | ||
return structure | ||
} | ||
return reactInlineStyleComponentPlugin | ||
} | ||
export default createPlugin() |
@@ -15,60 +15,2 @@ import * as t from '@babel/types' | ||
const prepareDynamicProps = (styles: StyleDefinitions) => { | ||
return Object.keys(styles).reduce((acc: any, key) => { | ||
const value = styles[key] | ||
if (typeof value === 'string' && value.startsWith('$props.')) { | ||
acc[key] = new ParsedASTNode( | ||
t.arrowFunctionExpression( | ||
[t.identifier('props')], | ||
t.memberExpression(t.identifier('props'), t.identifier(value.replace('$props.', ''))) | ||
) | ||
) | ||
} else { | ||
acc[key] = styles[key] | ||
} | ||
return acc | ||
}, {}) | ||
} | ||
const generateStyleTagStrings = ( | ||
content: ContentNode, | ||
nodesLookup: Record<string, t.JSXElement> | ||
) => { | ||
let accumulator: { [key: string]: any } = {} | ||
const { styles, children, key, repeat } = content | ||
if (styles) { | ||
const root = nodesLookup[key] | ||
const className = cammelCaseToDashCase(key) | ||
accumulator[className] = prepareDynamicProps(styles) | ||
// addClassStringOnJSXTag(root.node, className) | ||
addDynamicPropOnJsxOpeningTag(root, 'className', `classes['${className}']`, 'props') | ||
} | ||
if (repeat) { | ||
const items = generateStyleTagStrings(repeat.content, nodesLookup) | ||
accumulator = { | ||
...accumulator, | ||
...items, | ||
} | ||
} | ||
if (children) { | ||
children.forEach((child) => { | ||
if (typeof child === 'string') { | ||
return | ||
} | ||
// only call on children if they are not strings | ||
const items = generateStyleTagStrings(child, nodesLookup) | ||
accumulator = { | ||
...accumulator, | ||
...items, | ||
} | ||
}) | ||
} | ||
return accumulator | ||
} | ||
interface JSSConfig { | ||
@@ -154,1 +96,59 @@ styleChunkName?: string | ||
export default createPlugin() | ||
const prepareDynamicProps = (styles: StyleDefinitions) => { | ||
return Object.keys(styles).reduce((acc: any, key) => { | ||
const value = styles[key] | ||
if (typeof value === 'string' && value.startsWith('$props.')) { | ||
acc[key] = new ParsedASTNode( | ||
t.arrowFunctionExpression( | ||
[t.identifier('props')], | ||
t.memberExpression(t.identifier('props'), t.identifier(value.replace('$props.', ''))) | ||
) | ||
) | ||
} else { | ||
acc[key] = styles[key] | ||
} | ||
return acc | ||
}, {}) | ||
} | ||
const generateStyleTagStrings = ( | ||
content: ContentNode, | ||
nodesLookup: Record<string, t.JSXElement> | ||
) => { | ||
let accumulator: { [key: string]: any } = {} | ||
const { styles, children, key, repeat } = content | ||
if (styles) { | ||
const root = nodesLookup[key] | ||
const className = cammelCaseToDashCase(key) | ||
accumulator[className] = prepareDynamicProps(styles) | ||
// addClassStringOnJSXTag(root.node, className) | ||
addDynamicPropOnJsxOpeningTag(root, 'className', `classes['${className}']`, 'props') | ||
} | ||
if (repeat) { | ||
const items = generateStyleTagStrings(repeat.content, nodesLookup) | ||
accumulator = { | ||
...accumulator, | ||
...items, | ||
} | ||
} | ||
if (children) { | ||
children.forEach((child) => { | ||
if (typeof child === 'string') { | ||
return | ||
} | ||
// only call on children if they are not strings | ||
const items = generateStyleTagStrings(child, nodesLookup) | ||
accumulator = { | ||
...accumulator, | ||
...items, | ||
} | ||
}) | ||
} | ||
return accumulator | ||
} |
@@ -47,2 +47,3 @@ import { ComponentPlugin, ComponentPluginFactory } from '../../../types' | ||
path: 'prop-types', | ||
version: '15.7.2', | ||
} | ||
@@ -49,0 +50,0 @@ |
@@ -14,2 +14,43 @@ import preset from 'jss-preset-default' | ||
interface StyledJSXConfig { | ||
componentChunkName: string | ||
} | ||
export const createPlugin: ComponentPluginFactory<StyledJSXConfig> = (config) => { | ||
const { componentChunkName = 'react-component' } = config || {} | ||
const reactStyledJSXChunkPlugin: ComponentPlugin = async (structure) => { | ||
const { uidl, chunks } = structure | ||
const { content } = uidl | ||
const componentChunk = chunks.find((chunk) => chunk.name === componentChunkName) | ||
if (!componentChunk) { | ||
return structure | ||
} | ||
const jsxNodesLookup = componentChunk.meta.nodesLookup | ||
const styleJSXString = generateStyledJSXString(content, jsxNodesLookup) | ||
if (!styleJSXString || !styleJSXString.length) { | ||
return structure | ||
} | ||
const jsxASTNodeReference = generateStyledJSXTag(styleJSXString.join('\n')) | ||
const rootJSXNode = jsxNodesLookup[content.key] | ||
// We have the ability to insert the tag into the existig JSX structure, or do something else with it. | ||
// Here we take the JSX <style> tag and we insert it as the last child of the JSX structure | ||
// inside the React Component | ||
rootJSXNode.children.push(jsxASTNodeReference) | ||
return structure | ||
} | ||
return reactStyledJSXChunkPlugin | ||
} | ||
export default createPlugin() | ||
const prepareDynamicProps = (styles: StyleDefinitions) => { | ||
@@ -85,41 +126,1 @@ return Object.keys(styles).reduce((acc: any, key) => { | ||
} | ||
interface StyledJSXConfig { | ||
componentChunkName: string | ||
} | ||
export const createPlugin: ComponentPluginFactory<StyledJSXConfig> = (config) => { | ||
const { componentChunkName = 'react-component' } = config || {} | ||
const reactStyledJSXChunkPlugin: ComponentPlugin = async (structure) => { | ||
const { uidl, chunks } = structure | ||
const { content } = uidl | ||
const componentChunk = chunks.find((chunk) => chunk.name === componentChunkName) | ||
if (!componentChunk) { | ||
return structure | ||
} | ||
const jsxNodesLookup = componentChunk.meta.nodesLookup | ||
const styleJSXString = generateStyledJSXString(content, jsxNodesLookup) | ||
if (!styleJSXString || !styleJSXString.length) { | ||
return structure | ||
} | ||
const jsxASTNodeReference = generateStyledJSXTag(styleJSXString.join('\n')) | ||
const rootJSXNode = jsxNodesLookup[content.key] | ||
// We have the ability to insert the tag into the existig JSX structure, or do something else with it. | ||
// Here we take the JSX <style> tag and we insert it as the last child of the JSX structure | ||
// inside the React Component | ||
rootJSXNode.children.push(jsxASTNodeReference) | ||
return structure | ||
} | ||
return reactStyledJSXChunkPlugin | ||
} | ||
export default createPlugin() |
@@ -9,2 +9,48 @@ import preset from 'jss-preset-default' | ||
interface VueStyleChunkConfig { | ||
chunkName: string | ||
vueJSChunk: string | ||
vueTemplateChunk: string | ||
styleFileId: string | ||
} | ||
export const createPlugin: ComponentPluginFactory<VueStyleChunkConfig> = (config) => { | ||
const { | ||
chunkName = 'vue-component-style-chunk', | ||
vueTemplateChunk = 'vue-component-template-chunk', | ||
styleFileId = null, | ||
} = config || {} | ||
const vueComponentStyleChunkPlugin: ComponentPlugin = async (structure) => { | ||
const { uidl, chunks } = structure | ||
const { content } = uidl | ||
const templateChunk = chunks.filter((chunk) => chunk.name === vueTemplateChunk)[0] | ||
const templateLookup = templateChunk.meta.lookup | ||
const jssStylesArray = generateStyleTagStrings(content, templateLookup) | ||
chunks.push({ | ||
type: 'string', | ||
name: chunkName, | ||
meta: { | ||
fileId: styleFileId, | ||
}, | ||
wrap: styleFileId | ||
? undefined | ||
: (generatedContent) => { | ||
return `<style>\n${generatedContent}</style>` | ||
}, | ||
content: jssStylesArray.join('\n'), | ||
}) | ||
return structure | ||
} | ||
return vueComponentStyleChunkPlugin | ||
} | ||
export default createPlugin() | ||
const filterOutDynamicStyles = (styles: StyleDefinitions) => { | ||
@@ -74,47 +120,1 @@ if (!styles) { | ||
} | ||
interface VueStyleChunkConfig { | ||
chunkName: string | ||
vueJSChunk: string | ||
vueTemplateChunk: string | ||
styleFileId: string | ||
} | ||
export const createPlugin: ComponentPluginFactory<VueStyleChunkConfig> = (config) => { | ||
const { | ||
chunkName = 'vue-component-style-chunk', | ||
vueTemplateChunk = 'vue-component-template-chunk', | ||
styleFileId = null, | ||
} = config || {} | ||
const vueComponentStyleChunkPlugin: ComponentPlugin = async (structure) => { | ||
const { uidl, chunks } = structure | ||
const { content } = uidl | ||
const templateChunk = chunks.filter((chunk) => chunk.name === vueTemplateChunk)[0] | ||
const templateLookup = templateChunk.meta.lookup | ||
const jssStylesArray = generateStyleTagStrings(content, templateLookup) | ||
chunks.push({ | ||
type: 'string', | ||
name: chunkName, | ||
meta: { | ||
fileId: styleFileId, | ||
}, | ||
wrap: styleFileId | ||
? undefined | ||
: (generatedContent) => { | ||
return `<style>\n${generatedContent}</style>` | ||
}, | ||
content: jssStylesArray.join('\n'), | ||
}) | ||
return structure | ||
} | ||
return vueComponentStyleChunkPlugin | ||
} | ||
export default createPlugin() |
@@ -1,2 +0,2 @@ | ||
import { ComponentAssemblyLine, Builder, Resolver } from '../pipeline' | ||
import { AssemblyLine, Builder, Resolver } from '../pipeline' | ||
@@ -12,5 +12,4 @@ import { createPlugin as reactComponent } from '../plugins/react/react-base-component' | ||
import htmlMapping from '../../uidl-definitions/elements-mapping/html-mapping.json' | ||
import reactMapping from './elements-mapping.json' | ||
import { ComponentPlugin } from '../types' | ||
import { groupChunksByFileId } from './utils' | ||
import reactMapping from './react-mapping.json' | ||
import { ComponentPlugin, ReactComponentStylingFlavors } from '../types' | ||
import { ComponentUIDL, ElementsMapping } from '../../uidl-definitions/types' | ||
@@ -51,6 +50,6 @@ | ||
const stylePlugins: Record<string, ComponentPlugin> = { | ||
InlineStyles: configuredReactInlineStyles, | ||
StyledJSX: configuredReactStyledJSX, | ||
JSS: configuredReactJSS, | ||
CSSModules: configuredReactCSSModules, | ||
[ReactComponentStylingFlavors.InlineStyles]: configuredReactInlineStyles, | ||
[ReactComponentStylingFlavors.StyledJSX]: configuredReactStyledJSX, | ||
[ReactComponentStylingFlavors.JSS]: configuredReactJSS, | ||
[ReactComponentStylingFlavors.CSSModules]: configuredReactCSSModules, | ||
} | ||
@@ -75,3 +74,3 @@ | ||
}) | ||
const assemblyLine = new ComponentAssemblyLine([ | ||
const assemblyLine = new AssemblyLine([ | ||
configuredReactJSX, | ||
@@ -86,3 +85,3 @@ stylePlugin, | ||
const result = await assemblyLine.run(resolvedUidl) | ||
const chunksByFileId = groupChunksByFileId(result.chunks) | ||
const chunksByFileId = assemblyLine.groupChunksByFileId(result.chunks) | ||
@@ -89,0 +88,0 @@ const code = chunksLinker.link(chunksByFileId.default) |
@@ -1,2 +0,2 @@ | ||
import { ComponentAssemblyLine, Builder, Resolver } from '../pipeline' | ||
import { AssemblyLine, Builder, Resolver } from '../pipeline' | ||
@@ -14,3 +14,3 @@ import { createPlugin as reactComponent } from '../plugins/react/react-base-component' | ||
GeneratorOptions, | ||
ReactComponentFlavors, | ||
ReactComponentStylingFlavors, | ||
ComponentGenerator, | ||
@@ -22,35 +22,11 @@ CompiledComponent, | ||
import htmlMapping from '../../uidl-definitions/elements-mapping/html-mapping.json' | ||
import reactMapping from './elements-mapping.json' | ||
import { groupChunksByFileId } from './utils' | ||
import reactMapping from './react-mapping.json' | ||
interface ReactGeneratorFactoryParams { | ||
variation?: ReactComponentFlavors | ||
variation?: ReactComponentStylingFlavors | ||
customMapping?: ElementsMapping | ||
} | ||
const chooseStylePlugin = (variation: ReactComponentFlavors) => { | ||
switch (variation) { | ||
case ReactComponentFlavors.CSSModules: | ||
return reactCSSModules({ | ||
componentChunkName: 'react-component', | ||
}) | ||
case ReactComponentFlavors.InlineStyles: | ||
return reactInlineStyles({ | ||
componentChunkName: 'react-component', | ||
}) | ||
case ReactComponentFlavors.JSS: | ||
return reactJSS({ | ||
componentChunkName: 'react-component', | ||
importChunkName: 'import', | ||
exportChunkName: 'export', | ||
}) | ||
case ReactComponentFlavors.StyledJSX: | ||
return reactStyledJSX({ | ||
componentChunkName: 'react-component', | ||
}) | ||
} | ||
} | ||
const createReactGenerator = (params: ReactGeneratorFactoryParams = {}): ComponentGenerator => { | ||
const { variation = ReactComponentFlavors.CSSModules, customMapping = {} } = params | ||
const { variation = ReactComponentStylingFlavors.InlineStyles, customMapping = {} } = params | ||
@@ -62,3 +38,3 @@ const resolver = new Resolver() | ||
const assemblyLine = new ComponentAssemblyLine() | ||
const assemblyLine = new AssemblyLine() | ||
assemblyLine.addPlugin( | ||
@@ -92,3 +68,3 @@ reactComponent({ | ||
const chunksByFileId = groupChunksByFileId(result.chunks) | ||
const chunksByFileId = assemblyLine.groupChunksByFileId(result.chunks) | ||
@@ -113,2 +89,29 @@ const code = chunksLinker.link(chunksByFileId.default) | ||
const chooseStylePlugin = (variation: ReactComponentStylingFlavors) => { | ||
switch (variation) { | ||
case ReactComponentStylingFlavors.CSSModules: | ||
return reactCSSModules({ | ||
componentChunkName: 'react-component', | ||
}) | ||
case ReactComponentStylingFlavors.InlineStyles: | ||
return reactInlineStyles({ | ||
componentChunkName: 'react-component', | ||
}) | ||
case ReactComponentStylingFlavors.JSS: | ||
return reactJSS({ | ||
componentChunkName: 'react-component', | ||
importChunkName: 'import', | ||
exportChunkName: 'export', | ||
}) | ||
case ReactComponentStylingFlavors.StyledJSX: | ||
return reactStyledJSX({ | ||
componentChunkName: 'react-component', | ||
}) | ||
default: | ||
return reactInlineStyles({ | ||
componentChunkName: 'react-component', | ||
}) | ||
} | ||
} | ||
export default createReactGenerator |
import { createPlugin as importStatements } from '../plugins/common/import-statements' | ||
import { createPlugin as appRoutingPlugin } from '../plugins/react/react-app-routing' | ||
import { ComponentAssemblyLine, Builder, Resolver } from '../pipeline' | ||
import { AssemblyLine, Builder, Resolver } from '../pipeline' | ||
import { ComponentUIDL } from '../../uidl-definitions/types' | ||
import htmlMapping from '../../uidl-definitions/elements-mapping/html-mapping.json' | ||
import reactMapping from './elements-mapping.json' | ||
import reactMapping from './react-mapping.json' | ||
@@ -25,6 +25,3 @@ const createRouterComponentGenerator = () => { | ||
const assemblyLine = new ComponentAssemblyLine([ | ||
configureAppRouterComponent, | ||
configureImportStatements, | ||
]) | ||
const assemblyLine = new AssemblyLine([configureAppRouterComponent, configureImportStatements]) | ||
const chunksLinker = new Builder() | ||
@@ -31,0 +28,0 @@ |
@@ -154,2 +154,20 @@ import * as types from '@babel/types' | ||
export const addAttributeToJSXTag = ( | ||
jsxNode: types.JSXElement, | ||
attribute: { name: string; value?: any }, | ||
t = types | ||
) => { | ||
const nameOfAttribute = t.jsxIdentifier(attribute.name) | ||
let attributeDefinition | ||
if (typeof attribute.value === 'boolean') { | ||
attributeDefinition = t.jsxAttribute(nameOfAttribute) | ||
} else { | ||
attributeDefinition = t.jsxAttribute( | ||
nameOfAttribute, | ||
getProperAttributeValueAssignment(attribute.value) | ||
) | ||
} | ||
jsxNode.openingElement.attributes.push(attributeDefinition) | ||
} | ||
/** | ||
@@ -181,19 +199,2 @@ * node must be a AST node element of type JSXElement (babel-types) or | ||
} | ||
export const addAttributeToJSXTag = ( | ||
jsxNode: types.JSXElement, | ||
attribute: { name: string; value?: any }, | ||
t = types | ||
) => { | ||
const nameOfAttribute = t.jsxIdentifier(attribute.name) | ||
let attributeDefinition | ||
if (typeof attribute.value === 'boolean') { | ||
attributeDefinition = t.jsxAttribute(nameOfAttribute) | ||
} else { | ||
attributeDefinition = t.jsxAttribute( | ||
nameOfAttribute, | ||
getProperAttributeValueAssignment(attribute.value) | ||
) | ||
} | ||
jsxNode.openingElement.attributes.push(attributeDefinition) | ||
} | ||
@@ -200,0 +201,0 @@ /** |
import { StateDefinition } from '../../uidl-definitions/types' | ||
import { ASSETS_IDENTIFIER } from '../constants' | ||
@@ -43,1 +44,13 @@ /** | ||
} | ||
export const prefixPlaygroundAssetsURL = (prefix: string, originalString: string | undefined) => { | ||
if (!originalString || !originalString.startsWith(ASSETS_IDENTIFIER)) { | ||
return originalString | ||
} | ||
if (originalString.startsWith('/')) { | ||
return prefix + originalString | ||
} | ||
return `${prefix}/${originalString}` | ||
} |
import cheerio from 'cheerio' | ||
export const createXMLRoot = ( | ||
tagName: string, | ||
options = { selfClosing: false } | ||
): CheerioStatic => { | ||
export const createXMLRoot = (tagName: string, options = { selfClosing: false }): CheerioStatic => { | ||
const emptyDeclaration = options.selfClosing ? `<${tagName}/>` : `<${tagName}></${tagName}>` | ||
@@ -8,0 +5,0 @@ let result |
@@ -1,2 +0,2 @@ | ||
import { ComponentAssemblyLine, Builder, Resolver } from '../pipeline' | ||
import { AssemblyLine, Builder, Resolver } from '../pipeline' | ||
@@ -11,3 +11,3 @@ import { createPlugin as vueBaseComponent } from '../plugins/vue/vue-base-component' | ||
import htmlMapping from '../../uidl-definitions/elements-mapping/html-mapping.json' | ||
import vueMapping from './elements-mapping.json' | ||
import vueMapping from './vue-mapping.json' | ||
@@ -18,3 +18,3 @@ const createVueGenerator = ( | ||
const resolver = new Resolver({ ...htmlMapping, ...vueMapping, ...customMapping }) | ||
const assemblyLine = new ComponentAssemblyLine([ | ||
const assemblyLine = new AssemblyLine([ | ||
vueBaseComponent({ | ||
@@ -21,0 +21,0 @@ jsFileId: 'vuejs', |
@@ -1,2 +0,2 @@ | ||
import { ComponentAssemblyLine, Builder, Resolver } from '../pipeline' | ||
import { AssemblyLine, Builder, Resolver } from '../pipeline' | ||
@@ -10,7 +10,7 @@ import { createPlugin as createRouterPlugin } from '../plugins/vue/vue-router' | ||
import htmlMapping from '../../uidl-definitions/elements-mapping/html-mapping.json' | ||
import vueMapping from './elements-mapping.json' | ||
import vueMapping from './vue-mapping.json' | ||
const createVuePipeline = ({ customMapping }: GeneratorOptions = {}) => { | ||
const resolver = new Resolver({ ...htmlMapping, ...vueMapping, ...customMapping }) | ||
const assemblyLine = new ComponentAssemblyLine([ | ||
const assemblyLine = new AssemblyLine([ | ||
createRouterPlugin({ | ||
@@ -17,0 +17,0 @@ codeChunkName: 'vue-router', |
@@ -1,9 +0,9 @@ | ||
import reactProjectMapping from './elements-mapping.json' | ||
import reactProjectMapping from './react-project-mapping.json' | ||
import createRouterComponentGenerator from '../../component-generators/react/react-router' | ||
import createReactGenerator from '../../component-generators/react/react-component' | ||
import { ReactComponentFlavors } from '../../component-generators/types' | ||
import { ReactComponentStylingFlavors } from '../../component-generators/types' | ||
import { extractPageMetadata } from '../../component-generators/utils/uidl-utils' | ||
import { extractExternalDependencies, createManifestJSON } from '../utils/generator-utils' | ||
import { createPackageJSON, createManifestJSON } from '../utils/generator-utils' | ||
@@ -13,2 +13,3 @@ import { File, Folder, ProjectGeneratorOptions } from '../types' | ||
import { createHtmlIndexFile } from './utils' | ||
import { ASSETS_PREFIX, DEFAULT_OUTPUT_FOLDER, DEFAULT_PACKAGE_JSON } from './constants' | ||
@@ -18,3 +19,3 @@ export default async (uidl: ProjectUIDL, options: ProjectGeneratorOptions = {}) => { | ||
const reactGenerator = createReactGenerator({ | ||
variation: ReactComponentFlavors.CSSModules, | ||
variation: ReactComponentStylingFlavors.CSSModules, | ||
}) | ||
@@ -50,3 +51,3 @@ | ||
const distFolder: Folder = { | ||
name: options.distPath || 'dist', | ||
name: options.distPath || DEFAULT_OUTPUT_FOLDER, | ||
files: [], | ||
@@ -66,7 +67,6 @@ subFolders: [srcFolder], | ||
const stateDefinitions = root.stateDefinitions | ||
const assetsPrefix = '/static' | ||
const result = { | ||
outputFolder: distFolder, | ||
assetsPath: assetsPrefix, | ||
assetsPath: 'src' + ASSETS_PREFIX, | ||
} | ||
@@ -85,3 +85,3 @@ | ||
if (uidl.globals.manifest) { | ||
const manifestJSON = createManifestJSON(uidl.globals.manifest, uidl.name) | ||
const manifestJSON = createManifestJSON(uidl.globals.manifest, uidl.name, ASSETS_PREFIX) | ||
const manifestFile: File = { | ||
@@ -144,2 +144,3 @@ name: 'manifest', | ||
localDependenciesPrefix: '../components/', | ||
assetsPrefix: ASSETS_PREFIX, | ||
}) | ||
@@ -177,3 +178,5 @@ | ||
const component = components[componentName] | ||
const compiledComponent = await reactGenerator.generateComponent(component) | ||
const compiledComponent = await reactGenerator.generateComponent(component, { | ||
assetsPrefix: ASSETS_PREFIX, | ||
}) | ||
@@ -208,20 +211,20 @@ let cssFile: File | null = null | ||
const { sourcePackageJson } = options | ||
if (sourcePackageJson) { | ||
const externalDep = extractExternalDependencies(allDependencies) | ||
sourcePackageJson.dependencies = { | ||
...sourcePackageJson.dependencies, | ||
...externalDep, | ||
const packageJSON = createPackageJSON( | ||
sourcePackageJson || DEFAULT_PACKAGE_JSON, | ||
allDependencies, | ||
{ | ||
projectName: uidl.name, | ||
} | ||
) | ||
const packageFile: File = { | ||
name: 'package', | ||
extension: '.json', | ||
content: JSON.stringify(sourcePackageJson, null, 2), | ||
} | ||
distFolder.files.push(packageFile) | ||
const packageFile: File = { | ||
name: 'package', | ||
extension: '.json', | ||
content: JSON.stringify(packageJSON, null, 2), | ||
} | ||
distFolder.files.push(packageFile) | ||
return result | ||
} |
import { generator } from '../../component-generators/pipeline/builder/generators/html-to-string' | ||
import { createXMLNode, createXMLRoot } from '../../component-generators/utils/xml' | ||
import { ProjectUIDL } from '../../uidl-definitions/types' | ||
import { ASSETS_PREFIX } from './constants' | ||
import { prefixPlaygroundAssetsURL } from '../../component-generators/utils/uidl-utils' | ||
@@ -39,3 +41,4 @@ export const createHtmlIndexFile = (uidl: ProjectUIDL) => { | ||
Object.keys(metaItem).forEach((key) => { | ||
metaTag.attr(key, metaItem[key]) | ||
const prefixedURL = prefixPlaygroundAssetsURL(ASSETS_PREFIX, metaItem[key]) | ||
metaTag.attr(key, prefixedURL) | ||
}) | ||
@@ -46,7 +49,9 @@ headNode.append(metaTag) | ||
assets.forEach((asset) => { | ||
const assetPath = prefixPlaygroundAssetsURL(ASSETS_PREFIX, asset.path) | ||
// link stylesheet (external css, font) | ||
if ((asset.type === 'style' || asset.type === 'font') && asset.path) { | ||
if ((asset.type === 'style' || asset.type === 'font') && assetPath) { | ||
const linkTag = createXMLNode('link', { selfClosing: true }) | ||
linkTag.attr('rel', 'stylesheet') | ||
linkTag.attr('href', asset.path) | ||
linkTag.attr('href', assetPath) | ||
headNode.append(linkTag) | ||
@@ -68,4 +73,4 @@ } | ||
scriptTag.attr('type', 'text/javascript') | ||
if (asset.path) { | ||
scriptTag.attr('src', asset.path) | ||
if (assetPath) { | ||
scriptTag.attr('src', assetPath) | ||
if (asset.meta && asset.meta.defer) { | ||
@@ -88,6 +93,6 @@ scriptTag.attr('defer', true) | ||
// icon | ||
if (asset.type === 'icon' && asset.path) { | ||
if (asset.type === 'icon' && assetPath) { | ||
const iconTag = createXMLNode('link', { selfClosing: true }) | ||
iconTag.attr('rel', 'shortcut icon') | ||
iconTag.attr('href', asset.path) | ||
iconTag.attr('href', assetPath) | ||
if (typeof asset.meta === 'object') { | ||
@@ -94,0 +99,0 @@ const assetMeta = asset.meta |
import { Folder, File, ProjectGeneratorOptions } from '../types' | ||
import { ReactComponentFlavors } from '../../component-generators/types' | ||
import { ReactComponentStylingFlavors } from '../../component-generators/types' | ||
import { ProjectUIDL, ComponentDependency, ComponentUIDL } from '../../uidl-definitions/types' | ||
import { extractExternalDependencies, createManifestJSON } from '../utils/generator-utils' | ||
import { createManifestJSON, createPackageJSON } from '../utils/generator-utils' | ||
@@ -11,3 +11,4 @@ import createReactGenerator from '../../component-generators/react/react-component' | ||
import { createDocumentComponent } from './utils' | ||
import nextMapping from './elements-mapping.json' | ||
import nextMapping from './next-mapping.json' | ||
import { ASSETS_PREFIX, DEFAULT_OUTPUT_FOLDER, DEFAULT_PACKAGE_JSON } from './constants' | ||
@@ -17,3 +18,3 @@ export default async (uidl: ProjectUIDL, options: ProjectGeneratorOptions = {}) => { | ||
const reactGenerator = createReactGenerator({ | ||
variation: ReactComponentFlavors.StyledJSX, | ||
variation: ReactComponentStylingFlavors.StyledJSX, | ||
customMapping: nextMapping, | ||
@@ -42,3 +43,3 @@ }) | ||
const distFolder: Folder = { | ||
name: options.distPath || 'dist', | ||
name: options.distPath || DEFAULT_OUTPUT_FOLDER, | ||
files: [], | ||
@@ -55,3 +56,2 @@ subFolders: [pagesFolder, componentsFolder, staticFolder], | ||
let allDependencies: Record<string, ComponentDependency> = {} | ||
const assetsPrefix = '/static' | ||
const { components, root } = uidl | ||
@@ -63,3 +63,3 @@ const states = root.content.states | ||
outputFolder: distFolder, | ||
assetsPath: assetsPrefix, | ||
assetsPath: ASSETS_PREFIX.slice(1), | ||
} | ||
@@ -78,3 +78,3 @@ | ||
if (uidl.globals.manifest) { | ||
const manifestJSON = createManifestJSON(uidl.globals.manifest, uidl.name) | ||
const manifestJSON = createManifestJSON(uidl.globals.manifest, uidl.name, ASSETS_PREFIX) | ||
const manifestFile: File = { | ||
@@ -123,3 +123,3 @@ name: 'manifest', | ||
localDependenciesPrefix: '../components/', | ||
assetsPrefix, | ||
assetsPrefix: ASSETS_PREFIX, | ||
}) | ||
@@ -153,3 +153,3 @@ | ||
const compiledComponent = await reactGenerator.generateComponent(component, { | ||
assetsPrefix, | ||
assetsPrefix: ASSETS_PREFIX, | ||
}) | ||
@@ -177,19 +177,20 @@ const file: File = { | ||
const { sourcePackageJson } = options | ||
if (sourcePackageJson) { | ||
const externalDep = extractExternalDependencies(allDependencies) | ||
sourcePackageJson.dependencies = { | ||
...sourcePackageJson.dependencies, | ||
...externalDep, | ||
} | ||
const packageFile: File = { | ||
name: 'package', | ||
extension: '.json', | ||
content: JSON.stringify(sourcePackageJson, null, 2), | ||
const packageJSON = createPackageJSON( | ||
sourcePackageJson || DEFAULT_PACKAGE_JSON, | ||
allDependencies, | ||
{ | ||
projectName: uidl.name, | ||
} | ||
) | ||
distFolder.files.push(packageFile) | ||
const packageFile: File = { | ||
name: 'package', | ||
extension: '.json', | ||
content: JSON.stringify(packageJSON, null, 2), | ||
} | ||
distFolder.files.push(packageFile) | ||
return result | ||
} |
@@ -10,2 +10,4 @@ import { generator } from '../../component-generators/pipeline/builder/generators/js-ast-to-code' | ||
import { ProjectUIDL } from '../../uidl-definitions/types' | ||
import { ASSETS_PREFIX } from './constants' | ||
import { prefixPlaygroundAssetsURL } from '../../component-generators/utils/uidl-utils' | ||
@@ -47,3 +49,4 @@ export const createDocumentComponent = (uidl: ProjectUIDL, t = types) => { | ||
Object.keys(metaItem).forEach((key) => { | ||
addAttributeToJSXTag(metaTag, { name: key, value: metaItem[key] }) | ||
const metaValue = prefixPlaygroundAssetsURL(ASSETS_PREFIX, metaItem[key]) | ||
addAttributeToJSXTag(metaTag, { name: key, value: metaValue }) | ||
}) | ||
@@ -54,7 +57,9 @@ addChildJSXTag(headNode, metaTag) | ||
assets.forEach((asset) => { | ||
const assetPath = prefixPlaygroundAssetsURL(ASSETS_PREFIX, asset.path) | ||
// link stylesheet (external css, font) | ||
if ((asset.type === 'style' || asset.type === 'font') && asset.path) { | ||
if ((asset.type === 'style' || asset.type === 'font') && assetPath) { | ||
const linkTag = generateASTDefinitionForJSXTag('link') | ||
addAttributeToJSXTag(linkTag, { name: 'rel', value: 'stylesheet' }) | ||
addAttributeToJSXTag(linkTag, { name: 'href', value: asset.path }) | ||
addAttributeToJSXTag(linkTag, { name: 'href', value: assetPath }) | ||
addChildJSXTag(headNode, linkTag) | ||
@@ -77,4 +82,4 @@ } | ||
addAttributeToJSXTag(scriptTag, { name: 'type', value: 'text/javascript' }) | ||
if (asset.path) { | ||
addAttributeToJSXTag(scriptTag, { name: 'src', value: asset.path }) | ||
if (assetPath) { | ||
addAttributeToJSXTag(scriptTag, { name: 'src', value: assetPath }) | ||
if (asset.meta && asset.meta.defer) { | ||
@@ -101,6 +106,6 @@ addAttributeToJSXTag(scriptTag, { name: 'defer', value: true }) | ||
// icon | ||
if (asset.type === 'icon' && asset.path) { | ||
if (asset.type === 'icon' && assetPath) { | ||
const iconTag = generateASTDefinitionForJSXTag('link') | ||
addAttributeToJSXTag(iconTag, { name: 'rel', value: 'shortcut icon' }) | ||
addAttributeToJSXTag(iconTag, { name: 'href', value: asset.path }) | ||
addAttributeToJSXTag(iconTag, { name: 'href', value: assetPath }) | ||
@@ -107,0 +112,0 @@ if (typeof asset.meta === 'object') { |
@@ -16,3 +16,3 @@ import { ProjectUIDL, ElementsMapping } from '../../uidl-definitions/types' | ||
export interface ProjectGeneratorOptions { | ||
sourcePackageJson?: Record<string, any> | ||
sourcePackageJson?: PackageJSON | ||
distPath?: string | ||
@@ -29,1 +29,12 @@ customMapping?: ElementsMapping | ||
}> | ||
export interface PackageJSON { | ||
name: string | ||
description: string | ||
version: string | ||
main: string | ||
author: string | ||
license: string | ||
scripts?: Record<string, string> | ||
dependencies?: Record<string, string> | ||
} |
import { ComponentDependency, WebManifest } from '../../uidl-definitions/types' | ||
import { PackageJSON } from '../../project-generators/types' | ||
import { prefixPlaygroundAssetsURL } from '../../component-generators/utils/uidl-utils' | ||
// Only package dependencies are needed for the package.json file | ||
export const extractExternalDependencies = (dependencies: Record<string, ComponentDependency>) => { | ||
@@ -20,3 +21,7 @@ return Object.keys(dependencies) | ||
// Creates a manifest json with the UIDL having priority over the default values | ||
export const createManifestJSON = (manifest: WebManifest, projectName?: string) => { | ||
export const createManifestJSON = ( | ||
manifest: WebManifest, | ||
projectName: string, | ||
assetsPrefix?: string | ||
) => { | ||
const defaultManifest: WebManifest = { | ||
@@ -29,6 +34,33 @@ short_name: projectName, | ||
const icons = manifest.icons.map((icon) => { | ||
const src = prefixPlaygroundAssetsURL(assetsPrefix, icon.src) | ||
return { ...icon, src } | ||
}) | ||
return { | ||
...defaultManifest, | ||
...manifest, | ||
...{ icons }, | ||
} | ||
} | ||
export const createPackageJSON = ( | ||
packageJSONTemplate: PackageJSON, | ||
dependencies: Record<string, ComponentDependency>, | ||
overwrites: { | ||
projectName: string | ||
} | ||
): PackageJSON => { | ||
const { projectName } = overwrites | ||
const externalDep = extractExternalDependencies(dependencies) | ||
return { | ||
...packageJSONTemplate, | ||
name: projectName, | ||
dependencies: { | ||
...packageJSONTemplate.dependencies, | ||
...externalDep, | ||
}, | ||
} | ||
} |
@@ -9,3 +9,3 @@ import createVueGenerator from '../../component-generators/vue/vue-component' | ||
import vueProjectMapping from './elements-mapping.json' | ||
import vueProjectMapping from './vue-project-mapping.json' | ||
@@ -12,0 +12,0 @@ export default async (uidl: ProjectUIDL, options: ProjectGeneratorOptions = {}) => { |
@@ -7,3 +7,3 @@ import { File, Folder, ProjectGeneratorOptions } from '../types' | ||
import createVueGenerator from '../../component-generators/vue/vue-component' | ||
import nuxtMapping from './elements-mapping.json' | ||
import nuxtMapping from './nuxt-mapping.json' | ||
@@ -10,0 +10,0 @@ export default async (uidl: ProjectUIDL, options: ProjectGeneratorOptions = {}) => { |
@@ -141,7 +141,28 @@ { | ||
"video": { | ||
"type": "video" | ||
"type": "video", | ||
"children": [ | ||
"$children", | ||
"This browser does not support the video formats given" | ||
] | ||
}, | ||
"audio": { | ||
"type": "audio" | ||
"type": "audio", | ||
"children": [ | ||
"$children", | ||
"This browser does not support the audio formats given" | ||
] | ||
}, | ||
"picture": { | ||
"type": "picture", | ||
"children": [ | ||
"$children", | ||
"This browser does not support the image formats given" | ||
] | ||
}, | ||
"source": { | ||
"type": "source", | ||
"attrs": { | ||
"src": "$attrs.url" | ||
} | ||
}, | ||
"svg": { | ||
@@ -148,0 +169,0 @@ "type": "svg" |
@@ -10,8 +10,3 @@ export interface ProjectUIDL { | ||
meta: Array<Record<string, string>> | ||
assets: Array<{ | ||
type: string | ||
path?: string | ||
content?: string | ||
meta?: Record<string, any> | ||
}> | ||
assets: GlobalAsset[] | ||
manifest?: WebManifest | ||
@@ -24,2 +19,9 @@ variables?: Record<string, string> | ||
export interface GlobalAsset { | ||
type: string | ||
path?: string | ||
content?: string | ||
meta?: Record<string, any> | ||
} | ||
export interface ComponentUIDL { | ||
@@ -26,0 +28,0 @@ $schema?: string |
@@ -9,3 +9,3 @@ { | ||
"ordered-imports": false, | ||
"curly": false, | ||
"curly": true, | ||
"no-submodule-imports": false, | ||
@@ -12,0 +12,0 @@ "interface-name": [true, "never-prefix"], |
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
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
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
0
256
661054
14
269
13127
1