diff --git a/packages/server/src/api/controllers/application.js b/packages/server/src/api/controllers/application.js index 6f5bf7f1e1..1bc1be09b7 100644 --- a/packages/server/src/api/controllers/application.js +++ b/packages/server/src/api/controllers/application.js @@ -199,19 +199,19 @@ const createEmptyAppPackage = async (ctx, app) => { fs.mkdirpSync(newAppFolder) - const bulkDocs = [] + let screensAndLayouts = [] for (let layout of BASE_LAYOUTS) { const cloned = cloneDeep(layout) cloned._id = generateLayoutID() cloned.title = app.name - bulkDocs.push(recurseMustache(cloned, app)) + screensAndLayouts.push(recurseMustache(cloned, app)) } const homeScreen = cloneDeep(HOME_SCREEN) homeScreen._id = generateScreenID() - bulkDocs.push(homeScreen) - await db.bulkDocs(bulkDocs) + screensAndLayouts.push(homeScreen) - await compileStaticAssets(app._id) + screensAndLayouts = await compileStaticAssets(app._id, screensAndLayouts) + await db.bulkDocs(screensAndLayouts) return newAppFolder } diff --git a/packages/server/src/api/controllers/layout.js b/packages/server/src/api/controllers/layout.js index eae7b04d49..9e22fe027d 100644 --- a/packages/server/src/api/controllers/layout.js +++ b/packages/server/src/api/controllers/layout.js @@ -4,12 +4,10 @@ const compileStaticAssets = require("../../utilities/builder/compileStaticAssets exports.save = async function(ctx) { const db = new CouchDB(ctx.user.appId) - const appPackage = ctx.request.body + let layout = ctx.request.body - // remove special doc props which couch will complain about - delete appPackage.layout._css - appPackage.layout._id = appPackage.layout._id || generateLayoutID() - ctx.body = await db.put(appPackage.layout) - await compileStaticAssets(ctx.user.appId) + layout._id = layout._id || generateLayoutID() + layout = await compileStaticAssets(ctx.user.appId, layout) + ctx.body = await db.put(layout) ctx.status = 200 } diff --git a/packages/server/src/api/controllers/screen.js b/packages/server/src/api/controllers/screen.js index acf6df22f0..588b642156 100644 --- a/packages/server/src/api/controllers/screen.js +++ b/packages/server/src/api/controllers/screen.js @@ -1,5 +1,6 @@ const CouchDB = require("../../db") const { getScreenParams, generateScreenID } = require("../../db/utils") +const compileStaticAssets = require("../../utilities/builder/compileStaticAssets") const { AccessController } = require("../../utilities/security/accessLevels") exports.fetch = async ctx => { @@ -23,12 +24,12 @@ exports.fetch = async ctx => { exports.save = async ctx => { const appId = ctx.user.appId const db = new CouchDB(appId) - const screen = ctx.request.body + let screen = ctx.request.body if (!screen._id) { screen._id = generateScreenID() } - delete screen._css + screen = await compileStaticAssets(ctx.user.appId, screen) const response = await db.put(screen) ctx.message = `Screen ${screen.name} saved.` diff --git a/packages/server/src/utilities/builder/compileStaticAssets.js b/packages/server/src/utilities/builder/compileStaticAssets.js index a64c2f0b98..43c7309f5e 100644 --- a/packages/server/src/utilities/builder/compileStaticAssets.js +++ b/packages/server/src/utilities/builder/compileStaticAssets.js @@ -1,66 +1,63 @@ -const { ensureDir, constants, copyFile, writeFile } = require("fs-extra") +const { + ensureDir, + constants, + copyFile, + writeFile, + readdir, + readFile, +} = require("fs-extra") const { join } = require("../centralPath") const { budibaseAppsDir } = require("../budibaseDir") -const CouchDB = require("../../db") -const { getScreenParams, getLayoutParams } = require("../../db/utils") -async function getAppPackage(appId) { - const db = new CouchDB(appId) - let params = { - include_docs: true, - } - let [screens, layouts] = await Promise.all([ - db.allDocs(getScreenParams(null, params)), - db.allDocs(getLayoutParams(null, params)), - ]) - screens = screens.rows.map(row => row.doc) - layouts = layouts.rows.map(row => row.doc) - if (!screens) { - screens = [] - } - if (!layouts) { - layouts = [] - } - return { screens, layouts } -} +const CSS_DIRECTORY = "css" /** * Compile all the non-db static web assets that are required for the running of * a budibase application. This includes CSS, the JSON structure of the DOM and * the client library, a script responsible for reading the JSON structure * and rendering the application. - * @param {string} appId - id of the application we want to compile static assets for + * @param {string} appId id of the application we want to compile static assets for + * @param {array|object} assets a list of screens or screen layouts for which the CSS should be extracted and stored. */ -module.exports = async appId => { +module.exports = async (appId, assets) => { const publicPath = join(budibaseAppsDir(), appId, "public") - const pkg = await getAppPackage(appId) - await ensureDir(publicPath) - await buildCssBundle(publicPath, pkg) - await copyClientLib(publicPath) + for (let asset of Array.isArray(assets) ? assets : [assets]) { + await buildCssBundle(publicPath, asset) + await copyClientLib(publicPath) + // remove props that shouldn't be present when written to DB + if (asset._css) { + delete asset._css + } + } + return assets } /** * Reads the _css property of all screens and the screen layouts, and creates a singular CSS * bundle for the app at /public/bundle.css * @param {String} publicPath - path to the public assets directory of the budibase application - * @param {Object} pkg - app package information + * @param {Object} asset a single screen or screen layout which is being updated */ -const buildCssBundle = async (publicPath, pkg) => { +const buildCssBundle = async (publicPath, asset) => { + const cssPath = join(publicPath, CSS_DIRECTORY) let cssString = "" - for (let screen of pkg.screens || []) { - if (!screen._css) continue - if (screen._css.trim().length === 0) { - delete screen._css - continue - } - cssString += screen._css + // create a singular CSS file for this asset + const assetCss = asset._css ? asset._css.trim() : "" + if (assetCss.length !== 0) { + await ensureDir(cssPath) + await writeFile(join(cssPath, asset._id), assetCss) } - if (pkg.layout._css) cssString += pkg.layout._css + // bundle up all the CSS in the directory into one top level CSS file + const cssFiles = await readdir(cssPath) + for (let filename of cssFiles) { + const css = await readFile(filename) + cssString += css + } - writeFile(join(publicPath, "bundle.css"), cssString) + await writeFile(join(publicPath, "bundle.css"), cssString) } /**