mirror of https://github.com/Budibase/budibase.git
5 changed files with 433 additions and 0 deletions
@ -0,0 +1,134 @@ |
|||||
|
const { |
||||
|
appPackageFolder |
||||
|
} = require("../createAppPackage"); |
||||
|
const { componentLibraryInfo } = require("./componentLibraryInfo"); |
||||
|
const { |
||||
|
stat, ensureDir, pathExists, |
||||
|
constants, copyFile, writeFile, |
||||
|
readFile |
||||
|
} = require("fs-extra"); |
||||
|
const { join, resolve, dirname } = require("path"); |
||||
|
const sqrl = require('squirrelly'); |
||||
|
|
||||
|
module.exports = async (config, appname, pages, appdefinition) => { |
||||
|
|
||||
|
const appPath = appPackageFolder(config, appname); |
||||
|
|
||||
|
await buildClientAppDefinition( |
||||
|
config, appname, |
||||
|
appdefinition, |
||||
|
appPath, pages, "main"); |
||||
|
|
||||
|
await buildClientAppDefinition( |
||||
|
config, appname, |
||||
|
appdefinition, |
||||
|
appPath, pages, "unauthenticated") |
||||
|
|
||||
|
await buildIndexHtml( |
||||
|
config, appname, appPath, |
||||
|
pages, "main"); |
||||
|
|
||||
|
await buildIndexHtml( |
||||
|
config, appname, appPath, |
||||
|
pages, "unauthenticated"); |
||||
|
|
||||
|
await copyClientLib(appPath, "main"); |
||||
|
await copyClientLib(appPath, "unauthenticated"); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
const publicPath = (appPath, pageName) => join(appPath, "public", pageName); |
||||
|
const rootPath = (config, appname) => config.useAppRootPath ? `/${appname}` : ""; |
||||
|
|
||||
|
const copyClientLib = async (appPath, pageName) => { |
||||
|
var sourcepath = require.resolve("@budibase/client"); |
||||
|
var destPath = join(publicPath(appPath, pageName), "budibase-client.js"); |
||||
|
|
||||
|
await copyFile(sourcepath, destPath, constants.COPYFILE_FICLONE); |
||||
|
await copyFile(sourcepath + ".map", destPath + ".map", constants.COPYFILE_FICLONE); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
const buildIndexHtml = async (config, appname, appPath, pages, pageName) => { |
||||
|
|
||||
|
|
||||
|
const appPublicPath = publicPath(appPath, pageName); |
||||
|
const appRootPath = rootPath(config, appname); |
||||
|
|
||||
|
const stylesheetUrl = s => |
||||
|
s.indexOf('http://') === 0 || s.indexOf('https://') === 0 |
||||
|
? s |
||||
|
: `/${rootPath(config, appname)}/${s}`; |
||||
|
|
||||
|
const templateObj = { |
||||
|
title: pages[pageName].index.title || "Budibase App", |
||||
|
favicon: `${appRootPath}/${pages[pageName].index.favicon || "/_shared/favicon.png"}`, |
||||
|
stylesheets: (pages.stylesheets || []).map(stylesheetUrl), |
||||
|
appRootPath |
||||
|
} |
||||
|
|
||||
|
const indexHtmlTemplate = await readFile( |
||||
|
resolve(__dirname, "index.template.html"), "utf8"); |
||||
|
|
||||
|
const indexHtmlPath = join(appPublicPath, "index.html"); |
||||
|
|
||||
|
const indexHtml = sqrl.Render(indexHtmlTemplate, templateObj) |
||||
|
|
||||
|
await writeFile( |
||||
|
indexHtmlPath, |
||||
|
indexHtml, |
||||
|
{flag:"w+"}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
const buildClientAppDefinition = async (config, appname, appdefinition, appPath, pages, pageName) => { |
||||
|
|
||||
|
|
||||
|
const appPublicPath = publicPath(appPath, pageName); |
||||
|
const appRootPath = rootPath(config, appname); |
||||
|
|
||||
|
|
||||
|
const componentLibraries = []; |
||||
|
|
||||
|
for(let lib of pages.componentLibraries) { |
||||
|
const info = await componentLibraryInfo(appPath, lib); |
||||
|
const libFile = info.components._lib || "index.js"; |
||||
|
const source = join(info.libDir, libFile); |
||||
|
const moduleDir = join(appPublicPath, "lib", info.libDir.replace(appPath, "")); |
||||
|
const destPath = join(moduleDir, libFile); |
||||
|
|
||||
|
await ensureDir(dirname(destPath)); |
||||
|
|
||||
|
componentLibraries.push({ |
||||
|
importPath: destPath.replace(appPublicPath, "") |
||||
|
.replace(/\\/g, "/"), |
||||
|
libName: lib |
||||
|
}); |
||||
|
|
||||
|
let shouldCopy = !(await pathExists(destPath)); |
||||
|
if(!shouldCopy) { |
||||
|
const destStat = await stat(destPath); |
||||
|
const sourceStat = await stat(source); |
||||
|
shouldCopy = destStat.ctimeMs !== sourceStat.ctimeMs; |
||||
|
} |
||||
|
|
||||
|
if(shouldCopy) { |
||||
|
await copyFile(source, destPath, constants.COPYFILE_FICLONE); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
const filename = join(appPublicPath, "clientAppDefinition.js"); |
||||
|
|
||||
|
const clientAppDefObj = { |
||||
|
hierarchy: appdefinition.hierarchy, |
||||
|
componentLibraries: componentLibraries, |
||||
|
appRootPath: appRootPath, |
||||
|
props: appdefinition.props[pageName] |
||||
|
} |
||||
|
|
||||
|
await writeFile(filename, |
||||
|
`window['##BUDIBASE_APPDEFINITION##'] = ${JSON.stringify(clientAppDefObj)}`); |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,67 @@ |
|||||
|
const { |
||||
|
readJSON, exists |
||||
|
} = require("fs-extra"); |
||||
|
const { |
||||
|
resolve, join , dirname |
||||
|
} = require("path"); |
||||
|
|
||||
|
const getLibDir = (appPath, libname) => { |
||||
|
try { |
||||
|
const componentsFile = require.resolve( |
||||
|
join(libname, "components.json"), |
||||
|
{ paths: [appPath]}); |
||||
|
return dirname(componentsFile); |
||||
|
} catch(e) { |
||||
|
console.log(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const getComponentsFilepath = libPath => |
||||
|
resolve(libPath, "components.json"); |
||||
|
|
||||
|
module.exports.componentsFilepath = (appPath, libname) => |
||||
|
getComponentsFilepath(getLibDir(appPath, libname)); |
||||
|
|
||||
|
module.exports.componentLibraryInfo = async (appPath, libname) => { |
||||
|
|
||||
|
const libDir = getLibDir(appPath, libname); |
||||
|
const componentsPath = getComponentsFilepath(libDir); |
||||
|
|
||||
|
if(!await exists(componentsPath)) { |
||||
|
const e = new Error(`could not find components definition file at ${componentsPath}`); |
||||
|
e.statusCode = 404; |
||||
|
throw e; |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
const components = await readJSON(componentsPath); |
||||
|
const namespacedComponents = {_lib:components._lib}; |
||||
|
for(let cname in components) { |
||||
|
if(cname === "_lib" || cname == "_generators") continue; |
||||
|
const namespacedName = `${libname}/${cname}`; |
||||
|
components[cname].name = namespacedName; |
||||
|
namespacedComponents[namespacedName] = components[cname]; |
||||
|
} |
||||
|
|
||||
|
const namespacedGenerators = {} |
||||
|
if(components._generators) { |
||||
|
namespacedGenerators._lib=components._generators._lib || "generators.js"; |
||||
|
for(let gname in components._generators) { |
||||
|
if(gname === "_lib") continue; |
||||
|
const namespacedName = `${libname}/${gname}`; |
||||
|
components._generators[gname].name = namespacedName; |
||||
|
namespacedGenerators[namespacedName] = components._generators[gname]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return ({ |
||||
|
components: namespacedComponents, |
||||
|
generators: namespacedGenerators, |
||||
|
libDir, |
||||
|
componentsPath |
||||
|
}); |
||||
|
} catch(e) { |
||||
|
const err = `could not parse JSON - ${componentsPath} : ${e.message}`; |
||||
|
throw new Error(err); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,175 @@ |
|||||
|
const { |
||||
|
appPackageFolder, |
||||
|
appsFolder |
||||
|
} = require("../createAppPackage"); |
||||
|
const { |
||||
|
readJSON, writeJSON, readdir, |
||||
|
stat, ensureDir, rename, |
||||
|
unlink, rmdir |
||||
|
} = require("fs-extra"); |
||||
|
const { |
||||
|
join,dirname |
||||
|
} = require("path"); |
||||
|
const { $ } = require("@budibase/core").common; |
||||
|
const { |
||||
|
keyBy, intersection, map |
||||
|
} = require("lodash/fp"); |
||||
|
const {merge} = require("lodash"); |
||||
|
|
||||
|
const { componentLibraryInfo } = require("./componentLibraryInfo"); |
||||
|
const savePackage = require("./savePackage"); |
||||
|
const buildApp = require("./buildApp"); |
||||
|
|
||||
|
module.exports.savePackage = savePackage; |
||||
|
|
||||
|
const getPages = async (appPath) => await readJSON(`${appPath}/pages.json`); |
||||
|
const getAppDefinition = async (appPath) => await readJSON(`${appPath}/appDefinition.json`); |
||||
|
|
||||
|
module.exports.getPackageForBuilder = async (config, appname) => { |
||||
|
const appPath = appPackageFolder(config, appname); |
||||
|
|
||||
|
const pages = await getPages(appPath); |
||||
|
|
||||
|
return ({ |
||||
|
appDefinition: await getAppDefinition(appPath), |
||||
|
|
||||
|
accessLevels: await readJSON(`${appPath}/access_levels.json`), |
||||
|
|
||||
|
pages, |
||||
|
|
||||
|
components: await getComponents(appPath, pages), |
||||
|
|
||||
|
screens: keyBy("name")( |
||||
|
await fetchscreens(appPath)) |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
module.exports.getApps = async (config, master) => { |
||||
|
const dirs = await readdir(appsFolder(config)); |
||||
|
|
||||
|
return $(master.listApplications(), [ |
||||
|
map(a => a.name), |
||||
|
intersection(dirs) |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
const componentPath = (appPath, name) => |
||||
|
join(appPath, "components", name + ".json"); |
||||
|
|
||||
|
module.exports.saveScreen = async (config, appname, component) => { |
||||
|
const appPath = appPackageFolder(config, appname); |
||||
|
const compPath = componentPath(appPath, component.name); |
||||
|
await ensureDir(dirname(compPath)); |
||||
|
await writeJSON( |
||||
|
compPath, |
||||
|
component, |
||||
|
{encoding:"utf8", flag:"w", spaces:2}); |
||||
|
} |
||||
|
|
||||
|
module.exports.renameScreen = async (config, appname, oldName, newName) => { |
||||
|
const appPath = appPackageFolder(config, appname); |
||||
|
|
||||
|
const oldComponentPath = componentPath( |
||||
|
appPath, oldName); |
||||
|
|
||||
|
const newComponentPath = componentPath( |
||||
|
appPath, newName); |
||||
|
|
||||
|
await ensureDir(dirname(newComponentPath)); |
||||
|
await rename( |
||||
|
oldComponentPath, |
||||
|
newComponentPath); |
||||
|
} |
||||
|
|
||||
|
module.exports.deleteScreen = async (config, appname, name) => { |
||||
|
const appPath = appPackageFolder(config, appname); |
||||
|
const componentFile = componentPath(appPath, name); |
||||
|
await unlink(componentFile); |
||||
|
|
||||
|
const dir = dirname(componentFile); |
||||
|
if((await readdir(dir)).length === 0) { |
||||
|
await rmdir(dir); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
module.exports.componentLibraryInfo = async (config, appname, lib) => { |
||||
|
const appPath = appPackageFolder(config, appname); |
||||
|
return await componentLibraryInfo(appPath, lib); |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
const getComponents = async (appPath, pages ,lib) => { |
||||
|
|
||||
|
let libs; |
||||
|
if(!lib) { |
||||
|
pages = pages || await readJSON( |
||||
|
`${appPath}/pages.json`); |
||||
|
|
||||
|
if(!pages.componentLibraries) return []; |
||||
|
|
||||
|
libs = pages.componentLibraries; |
||||
|
} else { |
||||
|
libs = [lib]; |
||||
|
} |
||||
|
|
||||
|
const components = {}; |
||||
|
const generators = {}; |
||||
|
|
||||
|
for(let l of libs) { |
||||
|
const info = await componentLibraryInfo(appPath, l); |
||||
|
merge(components, info.components); |
||||
|
merge(generators, info.generators); |
||||
|
} |
||||
|
|
||||
|
if(components._lib) delete components._lib; |
||||
|
if(components._generators) delete components._generators; |
||||
|
|
||||
|
return {components, generators}; |
||||
|
} |
||||
|
|
||||
|
const fetchscreens = async (appPath, relativePath = "") => { |
||||
|
|
||||
|
const currentDir = join(appPath, "components", relativePath); |
||||
|
|
||||
|
const contents = await readdir(currentDir); |
||||
|
|
||||
|
const components = []; |
||||
|
|
||||
|
for(let item of contents) { |
||||
|
const itemRelativePath = join(relativePath, item); |
||||
|
const itemFullPath = join(currentDir, item); |
||||
|
const stats = await stat(itemFullPath); |
||||
|
|
||||
|
if(stats.isFile()) { |
||||
|
|
||||
|
if(!item.endsWith(".json")) continue; |
||||
|
|
||||
|
const component = |
||||
|
await readJSON(itemFullPath); |
||||
|
|
||||
|
component.name = itemRelativePath |
||||
|
.substring(0, itemRelativePath.length - 5) |
||||
|
.replace(/\\/g, "/"); |
||||
|
|
||||
|
component.props = component.props || {}; |
||||
|
|
||||
|
components.push(component); |
||||
|
} else { |
||||
|
const childComponents = await fetchscreens( |
||||
|
appPath, join(relativePath, item) |
||||
|
); |
||||
|
|
||||
|
for(let c of childComponents) { |
||||
|
components.push(c); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return components; |
||||
|
} |
||||
|
|
||||
|
module.exports.getComponents = getComponents; |
||||
@ -0,0 +1,32 @@ |
|||||
|
<!doctype html> |
||||
|
<html> |
||||
|
<head> |
||||
|
<meta charset='utf8'> |
||||
|
<meta name='viewport' content='width=device-width'> |
||||
|
|
||||
|
<title>{{ title }}</title> |
||||
|
<link rel='icon' type='image/png' href='{{ favicon }}'> |
||||
|
|
||||
|
<style> |
||||
|
html, body { |
||||
|
height: 100%; |
||||
|
width: 100%; |
||||
|
} |
||||
|
</style> |
||||
|
|
||||
|
{{ each(options.stylesheets) }} |
||||
|
<link rel='stylesheet' href='{{ @this }}'> |
||||
|
{{ /each }} |
||||
|
|
||||
|
<script src='{{ appRootPath }}/clientAppDefinition.js'></script> |
||||
|
<script src='{{ appRootPath }}/budibase-client.js'></script> |
||||
|
<script> |
||||
|
loadBudibase(); |
||||
|
</script> |
||||
|
|
||||
|
</head> |
||||
|
|
||||
|
<body id="app"> |
||||
|
|
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,25 @@ |
|||||
|
const { appPackageFolder } = require("../createAppPackage"); |
||||
|
const { writeJSON } = require("fs-extra"); |
||||
|
const buildApp = require("./buildApp"); |
||||
|
|
||||
|
module.exports = async (config, appname, pkg) => { |
||||
|
const appPath = appPackageFolder(config, appname); |
||||
|
await writeJSON( |
||||
|
`${appPath}/appDefinition.json`, |
||||
|
pkg.appDefinition, |
||||
|
{spaces:2}); |
||||
|
|
||||
|
await writeJSON( |
||||
|
`${appPath}/access_levels.json`, |
||||
|
pkg.accessLevels, |
||||
|
{spaces:2}); |
||||
|
|
||||
|
await writeJSON( |
||||
|
`${appPath}/pages.json`, |
||||
|
pkg.pages, |
||||
|
{spaces:2}); |
||||
|
|
||||
|
await buildApp( |
||||
|
config, appname, |
||||
|
pkg.pages, pkg.appDefinition); |
||||
|
} |
||||
Loading…
Reference in new issue