Skip to content

Commit

Permalink
Use esbuild for css and add multi-layout support
Browse files Browse the repository at this point in the history
BREAKING CHANGE: postcss transforms are removed in favor of esbuild css transforms. PCSS transforms will be re-added later.
  • Loading branch information
bcomnes committed Oct 5, 2023
1 parent b0e317d commit 398076d
Show file tree
Hide file tree
Showing 15 changed files with 297 additions and 148 deletions.
8 changes: 4 additions & 4 deletions bin.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@ function generateTreeData (cwd, src, dest, results) {
const treeStructure = {
label: `${join(cwdDir, srcDir)} => ${join(cwdDir, destDir)}`,
leaf: {
globalStyle: results?.siteData?.globalStyle?.basename,
globalClient: results?.siteData?.outputMaps?.js?.['global.client.js'],
globalStyle: results?.siteData?.globalStyle?.outputRelname,
globalClient: results?.siteData?.outputMaps?.outputRelname,
globalVars: results?.siteData?.globalVars?.basename,
rootLayout: results?.siteData?.rootLayout?.basename
},
Expand All @@ -166,8 +166,8 @@ function generateTreeData (cwd, src, dest, results) {
}

targetNode.leaf[page.page.basename] = join(page.path, page.outputName)
if (page.pageStyle) targetNode.leaf[page.pageStyle.basename] = join(page.path, page.pageStyle.basename)
if (page.clientBundle) targetNode.leaf[page.clientBundle.basename] = join(page.path, basename(results?.siteData?.outputMaps?.js?.[page.clientBundle.relname]))
if (page.pageStyle) targetNode.leaf[page.pageStyle.basename] = join(page.path, page.pageStyle.outputName ?? page.pageStyle.basename)
if (page.clientBundle) targetNode.leaf[page.clientBundle.basename] = join(page.path, page.clientBundle.outputName ?? page.clientBundle.basename)
if (page.pageVars) targetNode.leaf[page.pageVars.basename] = join(page.path, page.pageVars.basename)
}

Expand Down
119 changes: 119 additions & 0 deletions lib/build-esbuild/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { join, relative, basename } from 'path'
import esbuild from 'esbuild'
import { resolveVars } from '../build-pages/resolve-vars.js'

/**
* Build all of the bundles
* @param {string} src Path string of the site src
* @param {string} dest Path string of the site build dest
* @param {[type]} siteData [description]
* @return {[type]} [description]
*/
export async function buildEsbuild (src, dest, siteData, opts) {
const entryPoints = []
if (siteData.globalClient) entryPoints.push(join(src, siteData.globalClient.relname))
if (siteData.globalStyle) entryPoints.push(join(src, siteData.globalStyle.relname))

for (const page of siteData.pages) {
if (page.clientBundle) entryPoints.push(join(src, page.clientBundle.relname))
if (page.pageStyle) entryPoints.push(join(src, page.pageStyle.relname))
}

const browserVars = await resolveVars(siteData?.globalVars?.filepath, 'browser')

const define = {}

if (browserVars) {
for (const [k, v] of Object.entries(browserVars)) {
define[k] = JSON.stringify(v)
}
}

const mapOutputToEntry = {
name: 'mapOutputToEntry',
setup (build) {
build.onEnd(result => {
const ob = {}
Object.keys(result?.metafile?.outputs || {}).forEach(file => {
const { entryPoint } = result.metafile.outputs[file]
if (entryPoint) {
ob[relative(src, entryPoint)] = relative(dest, file)
}
})
result.outputMap = ob
})
}
}

const buildOpts = {
entryPoints,
logLevel: 'silent',
bundle: true,
write: true,
format: 'esm',
splitting: true,
sourcemap: true,
outdir: dest,
target: [
'esnext'
],
define,
metafile: true,
entryNames: '[dir]/[name]-[hash]',
chunkNames: 'chunks/[ext]/[name]-[hash]',
plugins: [mapOutputToEntry]
}

try {
// esbuild returns { errors:[], warnings: [] } already
const { outputMap, ...buildResults } = await esbuild.build(buildOpts)

// Add output names to siteData
for (const page of siteData.pages) {
if (page.pageStyle) {
const outputRelname = outputMap[page.pageStyle.relname]
if (outputRelname) {
page.pageStyle.outputRelname = outputRelname
page.pageStyle.outputName = basename(outputRelname)
}
}

if (page.clientBundle) {
const outputRelname = outputMap[page.clientBundle.relname]
if (outputRelname) {
page.clientBundle.outputRelname = outputRelname
page.clientBundle.outputName = basename(outputRelname)
}
}
}

if (siteData.globalClient) {
const outputRelname = outputMap[siteData.globalClient.relname]
if (outputRelname) {
siteData.globalClient.outputRelname = outputRelname
siteData.globalClient.outputName = basename(outputRelname)
}
}

if (siteData.globalStyle) {
const outputRelname = outputMap[siteData.globalStyle.relname]
if (outputRelname) {
siteData.globalStyle.outputRelname = outputRelname
siteData.globalStyle.outputName = basename(outputRelname)
}
}

return { ...buildResults, buildOpts, type: 'esbuild' }
} catch (err) {
const report = {
type: 'esbuild',
errors: [],
warnings: [],
buildOpts
}
const buildError = new Error('Error building JS+CSS with esbuild', { cause: err })
buildError.buildOpts = buildOpts
report.errors.push(buildError)
return report
}
}
82 changes: 0 additions & 82 deletions lib/build-js/index.js

This file was deleted.

54 changes: 39 additions & 15 deletions lib/build-pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,36 +59,57 @@ export async function buildPagesDirect (src, dest, siteData) {
}

const [
globalVars,
rootLayout
defaultVars,
globalVars
] = await Promise.allSettled([
resolveVars(siteData?.globalVars?.filepath),
resolveLayout(siteData?.rootLayout?.filepath)
resolveVars(join(__dirname, '../defaults/default.vars.js')),
resolveVars(siteData?.globalVars?.filepath)
])

if (globalVars.status === 'rejected') {
if (defaultVars.status === 'rejected') {
report.errors.push(
{
error: new Error('Encountered an error when resolving globalVars', {
cause: globalVars.reason
error: new Error('Encountered an error when resolving defaultVars', {
cause: defaultVars.reason
})
}
)
}

if (rootLayout.status === 'rejected') {
if (globalVars.status === 'rejected') {
report.errors.push(
{
error: new Error('Encountered an error when resolving rootLayout', {
cause: rootLayout.reason
error: new Error('Encountered an error when resolving globalVars', {
cause: globalVars.reason
})
}
)
}

const resolvedLayoutResults = await Promise.allSettled(
Object.entries(siteData.layouts).map(async ([name, layout]) => [name, await resolveLayout(layout.filepath)])
)

const resolvedLayouts = {}

for (const resolvedLayoutResult of resolvedLayoutResults) {
if (resolvedLayoutResult.status === 'rejected') {
report.errors.push(
{
error: new Error('Error resolving a layout', {
cause: defaultVars.reason
})
}
)
} else {
resolvedLayouts[resolvedLayoutResult.value[0]] = resolvedLayoutResult.value[1]
}
}

if (report.errors.length > 0) return report // Return early, these will all fail.

const globalClientPath = `/${siteData?.outputMaps?.js?.['global.client.js'] ?? 'global.client.js'}`
const globalClientPath = `/${siteData?.globalClient?.outputRelname ?? siteData?.globalClient?.relname}`
const globalStylePath = `/${siteData?.globalStyle?.outputRelname ?? siteData?.globalStyle?.relname}`

for (const page of siteData.pages) {
try {
Expand All @@ -97,17 +118,20 @@ export async function buildPagesDirect (src, dest, siteData) {
if (!builder) throw new Error(`Can't build ${page.path}. unimplemented type ${page.type}`)

// Supplement global vars with some global styles and clients
globalVars.value.styles = siteData.globalStyle ? ['/global.css'] : []
globalVars.value.styles = siteData.globalStyle ? [globalStylePath] : []
globalVars.value.scripts = siteData.globalClient ? [globalClientPath] : []

const buildResult = await builder({
src,
dest,
page,
globalVars: globalVars.value,
rootLayout: rootLayout.value,
outputMaps: siteData.outputMaps
layouts: resolvedLayouts,
globalVars: {
...defaultVars.value,
...globalVars.value
}
})

report.success.push(buildResult)
} catch (err) {
const buildError = new Error('Error building page', { cause: err })
Expand Down
22 changes: 17 additions & 5 deletions lib/build-pages/page-builders/create-page-builder.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { join, basename } from 'path'
import { join } from 'path'
import pretty from 'pretty'
import { writeFile, mkdir } from 'fs/promises'

Expand All @@ -14,7 +14,13 @@ export function createPageBuilder (builder) {
* @param {[type]} options.rootLayout [description]
* @return {[type]} [description]
*/
return async ({ src, dest, page, globalVars, rootLayout, outputMaps }) => {
return async ({
src,
dest,
page,
layouts,
globalVars
}) => {
const pageVars = await resolveVars(page.pageVars ? page.pageVars.filepath : null)
const { vars, pageLayout } = await builder({ page })

Expand All @@ -23,12 +29,18 @@ export function createPageBuilder (builder) {

const finalVars = Object.assign({}, globalVars, pageVars, vars)

if (page.pageStyle) finalVars.styles.push('./style.css')
if (page.clientBundle) finalVars.scripts.push(`./${basename(outputMaps.js[page.clientBundle.relname])}`)
if (page.pageStyle) {
finalVars.styles.push(`./${page.pageStyle.outputName ?? page.pageStyle.basename}`)
}
if (page.clientBundle) {
finalVars.scripts.push(`./${page.clientBundle.outputName ?? page.clientBundle.basename}`)
}

const output = await pageLayout(finalVars)

const pageOutput = await rootLayout({
const layout = layouts[finalVars.layout]

const pageOutput = await layout({
...finalVars,
children: output
})
Expand Down
4 changes: 2 additions & 2 deletions lib/build-pages/resolve-layout.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* layout resolve code
* @param {[type]} layoutPath [description]
* layout resolve code from an esm
* @param {string} layoutPath The string path to the layout
* @return {[type]} [description]
*/
export async function resolveLayout (layoutPath) {
Expand Down
19 changes: 19 additions & 0 deletions lib/build-templates/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// import { Worker } from 'worker_threads'
// import desm from 'desm'
// import { join } from 'path'

// import { resolveVars } from './resolve-vars.js'
// import { resolveLayout } from './resolve-layout.js'

// const __dirname = desm(import.meta.url)

/**
* Page builder glue. Most of the magic happens in the builders.
* @param {[type]} src [description]
* @param {[type]} dest [description]
* @param {[type]} siteData [description]
* @return {[type]} [description]
*/
export function buildTemplates (src, dest, siteData, opts) {
throw new Error('Not Implemented')
}

0 comments on commit 398076d

Please sign in to comment.