Skip to content

Commit

Permalink
refactor: throw on rebuild error in local dev
Browse files Browse the repository at this point in the history
  • Loading branch information
cossssmin committed Feb 13, 2023
1 parent f75bec4 commit e317e4c
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 164 deletions.
262 changes: 139 additions & 123 deletions src/commands/serve.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,137 +36,153 @@ const serve = async (env = 'local', config = {}) => {

const spinner = ora()

try {
await buildToFile(env, config)

let templates = get(config, 'build.templates')
templates = Array.isArray(templates) ? templates : [templates]

const templatePaths = [...new Set(templates.map(config => `${get(config, 'source', 'src')}/**`))]
const tailwindConfig = get(config, 'build.tailwind.config', 'tailwind.config.js')
const globalPaths = [
'src/**',
...new Set(get(config, 'build.browsersync.watch', []))
]

if (typeof tailwindConfig === 'string') {
globalPaths.push(tailwindConfig)
}

// Watch for Template file changes
browsersync()
.watch(templatePaths)
.on('change', async file => {
config = await getConfig(env, config)

if (config.events && typeof config.events.beforeCreate === 'function') {
await config.events.beforeCreate(config)
}

// Don't render if file type is not configured
// eslint-disable-next-line
const filetypes = templates.reduce((acc, template) => {
return [...acc, ...get(template, 'filetypes', ['html'])]
}, [])

if (!filetypes.includes(path.extname(file).slice(1))) {
return
}

if (get(config, 'build.console.clear')) {
clearConsole()
}

const start = new Date()

spinner.start('Building email...')
// Build all emails first
await buildToFile(env, config)

// Set some paths to watch
let templates = get(config, 'build.templates')
templates = Array.isArray(templates) ? templates : [templates]

const templatePaths = [...new Set(templates.map(config => `${get(config, 'source', 'src')}/**`))]
const tailwindConfig = get(config, 'build.tailwind.config', 'tailwind.config.js')
const globalPaths = [
'src/**',
...new Set(get(config, 'build.browsersync.watch', []))
]

if (typeof tailwindConfig === 'string') {
globalPaths.push(tailwindConfig)
}

renderToString(
await fs.readFile(file.replace(/\\/g, '/'), 'utf8'),
{
maizzle: merge(
config,
{
build: {
current: {
path: path.parse(file)
}
// Watch for Template file changes
browsersync()
.watch(templatePaths)
.on('change', async file => {
config = await getConfig(env, config)

if (config.events && typeof config.events.beforeCreate === 'function') {
await config.events.beforeCreate(config)
}

// Don't render if file type is not configured
// eslint-disable-next-line
const filetypes = templates.reduce((acc, template) => {
return [...acc, ...get(template, 'filetypes', ['html'])]
}, [])

if (!filetypes.includes(path.extname(file).slice(1))) {
return
}

// Clear console if enabled
if (get(config, 'build.console.clear')) {
clearConsole()
}

// Start the spinner
const start = new Date()
spinner.start('Building email...')

// Render the template
renderToString(
await fs.readFile(file.replace(/\\/g, '/'), 'utf8'),
{
maizzle: merge(
config,
{
build: {
current: {
path: path.parse(file)
}
}
),
...config.events
}
)
.then(async ({html, config}) => {
let source = ''
let dest = ''
let ext = ''

if (Array.isArray(config.build.templates)) {
const match = config.build.templates.find(template => template.source === path.parse(file).dir)
source = get(match, 'source')
dest = get(match, 'destination.path', 'build_local')
ext = get(match, 'destination.ext', 'html')
} else if (isObject(config.build.templates)) {
source = get(config, 'build.templates.source')
dest = get(config, 'build.templates.destination.path', 'build_local')
ext = get(config, 'build.templates.destination.ext', 'html')
}
),
...config.events
}
)
.then(async ({html, config}) => {
let source = ''
let dest = ''
let ext = ''

if (Array.isArray(config.build.templates)) {
const match = config.build.templates.find(template => template.source === path.parse(file).dir)
source = get(match, 'source')
dest = get(match, 'destination.path', 'build_local')
ext = get(match, 'destination.ext', 'html')
} else if (isObject(config.build.templates)) {
source = get(config, 'build.templates.source')
dest = get(config, 'build.templates.destination.path', 'build_local')
ext = get(config, 'build.templates.destination.ext', 'html')
}

const fileDir = path.parse(file).dir.replace(source, '')
const finalDestination = path.join(dest, fileDir, `${path.parse(file).name}.${ext}`)

await fs.outputFile(config.permalink || finalDestination, html)
})
.then(() => {
browsersync().reload()
spinner.succeed(`Compiled in ${(Date.now() - start) / 1000}s [${file}]`)
})
.catch(() => spinner.warn(`Received empty HTML, please save your file again [${file}]`))
const fileDir = path.parse(file).dir.replace(source, '')
const finalDestination = path.join(dest, fileDir, `${path.parse(file).name}.${ext}`)

await fs.outputFile(config.permalink || finalDestination, html)
})
.then(() => {
browsersync().reload()
spinner.succeed(`Compiled in ${(Date.now() - start) / 1000}s [${file}]`)
})
.catch(error => {
throw error
})
})

// Watch for changes in all other files
browsersync()
.watch(globalPaths, {ignored: templatePaths})
.on('change', () => buildToFile(env, config)
.then(() => browsersync().reload())
.catch(error => {
throw error
})

// Watch for changes in all other files
browsersync()
.watch(globalPaths, {ignored: templatePaths})
.on('change', () => buildToFile(env, config).then(() => browsersync().reload()))
.on('unlink', () => buildToFile(env, config).then(() => browsersync().reload()))

// Watch for changes in config files
browsersync()
.watch('config*.js')
.on('change', async file => {
const parsedEnv = path.parse(file).name.split('.')[1] || 'local'

Config
.getMerged(parsedEnv)
.then(config => buildToFile(parsedEnv, config).then(() => browsersync().reload()))
)
.on('unlink', () => buildToFile(env, config)
.then(() => browsersync().reload())
.catch(error => {
throw error
})

// Browsersync options
const baseDir = templates.map(t => t.destination.path)

// Initialize Browsersync
browsersync()
.init(
merge(
{
notify: false,
open: false,
port: 3000,
server: {
baseDir,
directory: true
},
tunnel: false,
ui: {port: 3001},
logFileChanges: false
)

// Watch for changes in config files
browsersync()
.watch('config*.js')
.on('change', async file => {
const parsedEnv = path.parse(file).name.split('.')[1] || 'local'

Config
.getMerged(parsedEnv)
.then(config => buildToFile(parsedEnv, config)
.then(() => browsersync().reload())
.catch(error => {
throw error
})
)
})

// Browsersync options
const baseDir = templates.map(t => t.destination.path)

// Initialize Browsersync
browsersync()
.init(
merge(
{
notify: false,
open: false,
port: 3000,
server: {
baseDir,
directory: true
},
get(config, 'build.browsersync', {})
), () => {})
} catch (error) {
spinner.fail(error)
throw error
}
tunnel: false,
ui: {port: 3001},
logFileChanges: false
},
get(config, 'build.browsersync', {})
), () => {})
}

module.exports = serve
57 changes: 57 additions & 0 deletions test/test-serve.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
const test = require('ava')
const fs = require('fs-extra')
const Maizzle = require('../src')

test.beforeEach(t => {
t.context.folder = '_temp_' + Math.random().toString(36).slice(2, 9)
t.context.log = console.log()
})

test.afterEach.always(async t => {
if (t.context.folder) {
await fs.remove(t.context.folder)
delete t.context.folder
}
})

test('throws if it cannot spin up local development server', async t => {
// Should throw because there are no template sources in `build.templates`
await t.throwsAsync(async () => {
await Maizzle.serve('local', {})
}, {instanceOf: Error})
})

test('local server does not compile unwanted file types', async t => {
await Maizzle.serve('local', {
build: {
console: {
clear: true
},
browsersync: {
ui: false
},
templates: {
source: 'test/stubs/templates',
destination: {
path: `${t.context.folder}`
}
}
},
events: {
beforeCreate(config) {
config.foo = 'bar'
}
}
})

t.true(await fs.pathExists(`${t.context.folder}`))
t.true(await fs.pathExists(`${t.context.folder}/2.test`))

// Tests watching changes to files
await fs.outputFile('test/stubs/templates/2.html', '<div class="inline">html modified</div>')
t.is(await fs.readFile('test/stubs/templates/2.html', 'utf8'), '<div class="inline">html modified</div>')

// Don't trigger rebuilds on files not in `filetypes`
await fs.outputFile('test/stubs/templates/2.test', 'test')
t.is(await fs.readFile('test/stubs/templates/2.test', 'utf8'), 'test')
})
41 changes: 0 additions & 41 deletions test/test-todisk.js
Original file line number Diff line number Diff line change
Expand Up @@ -351,47 +351,6 @@ test('warns if a template cannot be rendered and `fail` option is `silent`', asy
t.false(files.includes('empty.html'))
})

test('local server does not compile unwanted file types', async t => {
await Maizzle.serve('local', {
build: {
console: {
clear: true
},
browsersync: {
ui: false
},
templates: {
source: 'test/stubs/templates',
destination: {
path: `${t.context.folder}`
}
}
},
events: {
beforeCreate(config) {
config.foo = 'bar'
}
}
})

t.true(await fs.pathExists(`${t.context.folder}`))
t.true(await fs.pathExists(`${t.context.folder}/2.test`))

// Tests watching changes to files
await fs.outputFile('test/stubs/templates/2.html', '<div class="inline">html modified</div>')
t.is(await fs.readFile('test/stubs/templates/2.html', 'utf8'), '<div class="inline">html modified</div>')

// Don't trigger rebuilds on files not in `filetypes`
await fs.outputFile('test/stubs/templates/2.test', 'test')
t.is(await fs.readFile('test/stubs/templates/2.test', 'utf8'), 'test')
})

test('throws if it cannot spin up local development server', async t => {
await t.throwsAsync(async () => {
await Maizzle.serve('local', {})
}, {instanceOf: Error})
})

test('`templates.source` undefined', async t => {
await t.throwsAsync(async () => {
await Maizzle.build('maizzle-ci')
Expand Down

0 comments on commit e317e4c

Please sign in to comment.