Skip to content

Commit

Permalink
feat(cli): beter CLI, better config handling
Browse files Browse the repository at this point in the history
  • Loading branch information
estrattonbailey committed May 10, 2021
1 parent 9ab4c69 commit 01eacd7
Show file tree
Hide file tree
Showing 14 changed files with 281 additions and 285 deletions.
76 changes: 38 additions & 38 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,17 @@ const pkg = require('./package.json')

const { CONFIG_DEFAULT } = require('./lib/constants')
const { log } = require('./lib/log')
const globalConfig = require('./lib/config')
const { createConfig, getConfigFile } = require('./lib/config')
const { watch } = require('./lib/watch')
const { build } = require('./lib/build')
const { serve } = require('./lib/serve')

function warnOnBadGlob (output) {
if (/\.(js|jsx|ts|tsx)$/.test(output)) {
const msg = ` Your specified output '${output}' looks like a file. Maybe you need surround your file glob with quotes?`
const issue = ` More info here: https://github.com/sure-thing/presta/issues/15`
log(`${c.yellow(`presta`)}\n\n${msg}\n\n${issue}\n`)
exit()
}
}

function registerRuntime (options = {}) {
require('module-alias').addAliases({
'@': process.cwd(),
'presta:internal': __dirname // wherever this is running from
})

require('esbuild-register/dist/node').register(options)
}

Expand All @@ -32,33 +28,36 @@ prog
.version(pkg.version)
.option(
'--config, -c',
`Path to a config file. (default /${CONFIG_DEFAULT})`
`Path to a config file — defaults to ${CONFIG_DEFAULT}`
)
.option(
'--output, -o',
`Specify output directory for built files — defaults to ./build`
)
.option(
'--assets, -a',
`Specify static asset directory — defaults to ./public`
)
.option('--assets, -a', `Specify static asset directory. (default /public)`)

prog
.command(
'build [files] [output]',
'Render files(s) to output directory (defaults to ./build)',
{ default: true }
)
.command('build', 'Render files(s) to output directory.', { default: true })
.example(`build`)
.example(`build files/**/*.js build`)
.example(`build files/**/*.js`)
.example(`build -c ${CONFIG_DEFAULT}`)
.action(async (files, output, opts) => {
.action(async opts => {
process.env.PRESTA_ENV = 'production'

registerRuntime()

console.clear()

warnOnBadGlob(output)

const config = globalConfig.create({
...opts,
files,
output,
env: 'production'
const config = createConfig({
env: 'production',
configFile: getConfigFile(opts.config),
cliArgs: {
...opts,
files: opts._
}
})

log(`${c.blue('~ presta build')}\n`)
Expand All @@ -67,26 +66,27 @@ prog
})

prog
.command('watch [files] [output]')
.option('--no-serve, -n', `Don't serve output directory`, false)
.describe('Watch and build page(s) to output directory')
.command('watch')
.option('--no-serve, -n', `Do not run local dev server.`, false)
.describe('Watch and build files(s) to output directory')
.example(`watch`)
.example(`watch files/**/*.js build`)
.example(`watch ./files/**/*.js`)
.example(`watch ./files/**/*.js -o ./out`)
.example(`watch -c ${CONFIG_DEFAULT}`)
.action(async (files, output, opts) => {
.action(async opts => {
process.env.PRESTA_ENV = 'development'

registerRuntime()

console.clear()

warnOnBadGlob(output)

const config = globalConfig.create({
...opts,
files,
output,
env: 'development'
const config = createConfig({
env: 'development',
configFile: getConfigFile(opts.config),
cliArgs: {
...opts,
files: opts._
}
})

if (!opts.n) {
Expand Down
1 change: 1 addition & 0 deletions docs/src/pages/Docs.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { Github } from '@/src/icons/Github'
import { Logo } from '@/src/components/Logo'

export async function getStaticPaths () {
// const [file] = source(path.resolve(__dirname, '../content/docs.md'))
return ['/docs']
}

Expand Down
8 changes: 4 additions & 4 deletions lib/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ async function build (config, options = {}) {
let dynamicTime = 0
let copyTime = 0

await Promise.all([
await Promise.allSettled([
(async () => {
if (staticIds.length) {
const time = timer()
Expand Down Expand Up @@ -60,12 +60,12 @@ async function build (config, options = {}) {
}
})(),
(async () => {
if (fs.existsSync(config.assets)) {
if (fs.existsSync(config.merged.assets)) {
const time = timer()

fs.copySync(
config.assets,
path.join(config.output, OUTPUT_STATIC_DIR) // TODO put in config?
config.merged.assets,
path.join(config.merged.output, OUTPUT_STATIC_DIR)
)

copyTime = time()
Expand Down
4 changes: 2 additions & 2 deletions lib/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ const { OUTPUT_DYNAMIC_PAGES_ENTRY } = require('./constants')

function compile (config) {
return buildSync({
entryPoints: [path.join(config.output, OUTPUT_DYNAMIC_PAGES_ENTRY)],
outfile: path.join(config.output, 'functions', 'presta.js'),
entryPoints: [path.join(config.merged.output, OUTPUT_DYNAMIC_PAGES_ENTRY)],
outfile: path.join(config.merged.output, 'functions', 'presta.js'),
bundle: true,
platform: 'node',
target: ['node12'],
Expand Down
153 changes: 64 additions & 89 deletions lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,108 +6,72 @@ const { debug } = require('./debug')
const { log } = require('./log')

const cwd = process.cwd()

function makeConfigPathsAbsolute (config, cwd) {
if (config.files)
config.files = [].concat(config.files).map(p => path.resolve(p))
if (config.output) config.output = path.resolve(config.output)
if (config.assets) config.assets = path.resolve(config.assets)

return config
const pid = process.pid
const manager = { [pid]: { cliArgs: {}, configFile: {} } }

function resolvePaths (obj) {
if (obj.files) obj.files = [].concat(obj.files).map(p => path.resolve(cwd, p))
if (obj.output) obj.output = path.resolve(cwd, obj.output)
if (obj.assets) obj.assets = path.resolve(cwd, obj.assets)
return obj
}

/*
* Don't write to these internal properties:
* cliArgs - the raw CLI args
* configFile - the full config file, if present
*/
function create (cliArgs) {
// leave this here in case we want it to be user-configurable
require('module-alias').addAliases({
'@': cwd,
'presta:internal': path.join(__dirname, '..')
})

let configFile = {}
let configFilepath = path.resolve(cliArgs.config || CONFIG_DEFAULT)

function getConfigFile (filepath) {
/*
* Since we want to let the user know right away if their config has issues,
* just try to require it right away
*/
try {
configFile = require(configFilepath)
return require(path.resolve(filepath || CONFIG_DEFAULT))
} catch (e) {
// if user specified a file, must be a syntax error
if (cliArgs.config) {
log(`${c.red('~ error')} ${configFilepath}\n\n > ${e.stack || e}\n`)
if (!!filepath) {
log(`${c.red('~ error')} ${filepath}\n\n > ${e.stack || e}\n`)
}

configFile = {}
configFilepath = undefined
return {}
}

cliArgs = makeConfigPathsAbsolute(cliArgs, cwd)
configFile = makeConfigPathsAbsolute(configFile, cwd)

const config = merge(
{
env: cliArgs.env,
cliArgs: cliArgs,
cwd,
configFilepath,
defaultConfigFilepath: path.join(cwd, CONFIG_DEFAULT)
},
configFile
)

debug('config', config)

return config
}

/*
* Merge a new presta.config.js file with the existing CLI args
* and internal config
*/
function merge (prev, next) {
const output = path.resolve(
prev.cliArgs.output || next.output || path.resolve(prev.cwd, 'build')
)
const config = {
...prev,
...next,
configFile: makeConfigPathsAbsolute(next, prev.cwd),
files: [].concat(prev.cliArgs.files || []).concat(next.files || []),
output,
assets:
prev.cliArgs.assets || next.assets || path.resolve(prev.cwd, 'public'),
dynamicEntryFilepath: path.join(output, OUTPUT_DYNAMIC_PAGES_ENTRY)
function createConfig ({
env = manager[pid].env,
configFile = manager[pid].configFile,
cliArgs = manager[pid].cliArgs
}) {
configFile = resolvePaths({ ...configFile }) // clone read-only obj
cliArgs = resolvePaths(cliArgs)

const merged = {
output: cliArgs.output || configFile.output || path.resolve('build'),
assets: cliArgs.assets || configFile.assets || path.resolve('public'),
files:
cliArgs.files && cliArgs.files.length
? cliArgs.files
: configFile.files
? [].concat(configFile.files)
: []
}

return makeConfigPathsAbsolute(config, prev.cwd)
}
manager[pid] = {
env,
cwd,
configFile,
cliArgs,
merged,
configFilepath: path.resolve(cliArgs.config || CONFIG_DEFAULT),
dynamicEntryFilepath: path.join(merged.output, OUTPUT_DYNAMIC_PAGES_ENTRY)
}

/*
* Clean previous presta.config.js values from the active
* internal config object
*/
function unmerge (active) {
const output = active.cliArgs.output || path.resolve(active.cwd, 'build')
return manager[pid]
}

const config = {
...active,
configFile: {},
files: active.files.filter(
p => ![].concat(active.configFile.files || []).find(pp => pp === p)
),
output,
assets: active.cliArgs.assets || path.resolve(active.cwd, 'public'),
configFilepath: undefined,
dynamicEntryFilepath: path.join(output, OUTPUT_DYNAMIC_PAGES_ENTRY)
}
function removeConfigValues () {
manager[pid] = createConfig({
...manager[pid],
configFile: {}
})

return config
return manager[pid]
}

/*
Expand All @@ -117,15 +81,26 @@ function unmerge (active) {
function serialize (config) {
return JSON.stringify({
cwd: config.cwd,
files: config.files,
output: config.output,
assets: config.assets
files: config.merged.files,
output: config.merged.output,
assets: config.merged.assets
})
}

function getCurrentConfig () {
return manager[pid]
}

function clearCurrentConfig () {
manager[pid] = { cliArgs: {}, configFile: {} }
}

module.exports = {
create,
merge,
unmerge,
getCurrentConfig,
clearCurrentConfig,
resolvePaths,
getConfigFile,
createConfig,
removeConfigValues,
serialize
}
10 changes: 7 additions & 3 deletions lib/createDynamicEntry.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
const fs = require('fs-extra')
const path = require('path')

const { CONFIG_DEFAULT } = require('./constants')
const { debug } = require('./debug')
const { serialize } = require('./config')

function template (sourceFiles, config) {
const presta = config.env === 'production' ? 'presta' : 'presta:internal'
const configFilepath = Object.keys(config.configFile).length
? config.configFilepath
: undefined

return `import { createRouter } from '${presta}/lib/router';
import { createHandler } from '${presta}/lib/handler';
${
config.configFilepath
? `import * as userConfig from '${config.configFilepath}'`
configFilepath
? `import * as userConfig from '${configFilepath}'`
: `const userConfig = {}`
}
${sourceFiles
Expand All @@ -29,7 +33,7 @@ export const handler = createHandler(router, config);
}

function createDynamicEntry (sourceFiles, config) {
const entryFile = path.join(config.dynamicEntryFilepath)
const entryFile = config.dynamicEntryFilepath

fs.outputFileSync(entryFile, template(sourceFiles, config))

Expand Down

0 comments on commit 01eacd7

Please sign in to comment.