1 | import fs from 'fs'
|
2 | import path from 'path'
|
3 |
|
4 | import * as logger from './log'
|
5 | import { createEmitter, createEmitHook, createOnHook } from './createEmitter'
|
6 | import { setCurrentPrestaInstance, getCurrentPrestaInstance } from './currentPrestaInstance'
|
7 | import { Presta, Config, CLI, Callable } from './types'
|
8 | import { Env } from './constants'
|
9 |
|
10 | const defaultConfigFilepath = 'presta.config.js'
|
11 |
|
12 | function resolveAbsolutePaths(
|
13 | config: {
|
14 | files?: string | string[]
|
15 | output?: string
|
16 | assets?: string
|
17 | },
|
18 | { cwd }: { cwd: string }
|
19 | ) {
|
20 | if (config.files) config.files = ([] as string[]).concat(config.files).map((p) => path.resolve(cwd, p))
|
21 | if (config.output) config.output = path.resolve(cwd, config.output)
|
22 | if (config.assets) config.assets = path.resolve(cwd, config.assets)
|
23 | return config
|
24 | }
|
25 |
|
26 | /**
|
27 | * @private
|
28 | */
|
29 | export function _clearCurrentConfig() {
|
30 | // @ts-ignore
|
31 | global.__presta__ = {
|
32 | pid: process.pid,
|
33 | cwd: process.cwd(),
|
34 | env: Env.PRODUCTION,
|
35 | }
|
36 | }
|
37 |
|
38 | /**
|
39 | * Fetch a config file. If one was specified by the user, let them know if
|
40 | * anything goes wrong. Outside watch mode, this should exit(1) if the user
|
41 | * provided a config and there was an error
|
42 | */
|
43 | export function getConfigFile(filepath?: string, shouldExit: boolean = false) {
|
44 | const fp = path.resolve(filepath || defaultConfigFilepath)
|
45 |
|
46 | try {
|
47 | return require(fp)
|
48 | } catch (e) {
|
49 | const exists = fs.existsSync(fp)
|
50 |
|
51 | // config file exists, should log error, otherwise ignore missing file
|
52 | if (exists) {
|
53 | logger.error({
|
54 | label: 'error',
|
55 | error: e as Error,
|
56 | })
|
57 |
|
58 | // we're not in watch mode, exit build
|
59 | if (shouldExit) process.exit(1)
|
60 | }
|
61 |
|
62 | return {}
|
63 | }
|
64 | }
|
65 |
|
66 | /**
|
67 | * Creates a new instance _without_ any values provided by the config file.
|
68 | * This is used when the user deletes their config file.
|
69 | */
|
70 | export async function removeConfigValues() {
|
71 | logger.debug({
|
72 | label: 'debug',
|
73 | message: `config file values cleared`,
|
74 | })
|
75 |
|
76 | return setCurrentPrestaInstance(
|
77 | await createConfig({
|
78 | ...getCurrentPrestaInstance(),
|
79 | config: {},
|
80 | })
|
81 | )
|
82 | }
|
83 |
|
84 | export async function createConfig({
|
85 | cwd = process.cwd(),
|
86 | env = getCurrentPrestaInstance().env,
|
87 | config = {},
|
88 | cli = {},
|
89 | }: {
|
90 | cwd?: string
|
91 | env?: string
|
92 | config?: Partial<Config>
|
93 | cli?: Partial<CLI>
|
94 | }) {
|
95 | config = resolveAbsolutePaths({ ...config }, { cwd }) // clone read-only obj
|
96 | cli = resolveAbsolutePaths({ ...cli }, { cwd })
|
97 |
|
98 | // combined config, preference to CLI args
|
99 | const merged = {
|
100 | output: path.resolve(cwd, cli.output || config.output || 'build'),
|
101 | assets: path.resolve(cli.assets || config.assets || 'public'),
|
102 | files: cli.files && cli.files.length ? cli.files : config.files ? ([] as string[]).concat(config.files) : [],
|
103 | }
|
104 | const port = cli.port ? parseInt(cli.port) : config.port || 4000
|
105 |
|
106 | const previous = getCurrentPrestaInstance()
|
107 | // only create once
|
108 | const emitter = previous.events || createEmitter()
|
109 |
|
110 | // deregister old events
|
111 | emitter.clear()
|
112 |
|
113 | // set instance
|
114 | const next: Presta = setCurrentPrestaInstance({
|
115 | ...previous,
|
116 | ...merged,
|
117 | env,
|
118 | cwd,
|
119 | port,
|
120 | debug: cli.debug || getCurrentPrestaInstance().debug,
|
121 | configFilepath: path.resolve(cli.config || defaultConfigFilepath),
|
122 | staticOutputDir: path.join(merged.output, 'static'),
|
123 | functionsOutputDir: path.join(merged.output, 'functions'),
|
124 | functionsManifest: path.join(merged.output, 'routes.json'),
|
125 | events: emitter,
|
126 | hooks: {
|
127 | emitPostBuild(props) {
|
128 | emitter.emit('postBuild', props)
|
129 | },
|
130 | onPostBuild(cb) {
|
131 | return emitter.on('postBuild', cb)
|
132 | },
|
133 | emitBuildFile(props) {
|
134 | emitter.emit('buildFile', props)
|
135 | },
|
136 | onBuildFile(cb) {
|
137 | return emitter.on('buildFile', cb)
|
138 | },
|
139 | emitBrowserRefresh() {
|
140 | emitter.emit('browserRefresh')
|
141 | },
|
142 | onBrowserRefresh(cb) {
|
143 | return emitter.on('browserRefresh', cb)
|
144 | },
|
145 | },
|
146 | })
|
147 |
|
148 | if (config.plugins) {
|
149 | await Promise.all(
|
150 | config.plugins.map((p) => {
|
151 | try {
|
152 | return p(getCurrentPrestaInstance)
|
153 | } catch (e) {
|
154 | logger.error({
|
155 | label: 'error',
|
156 | error: e as Error,
|
157 | })
|
158 | }
|
159 | })
|
160 | )
|
161 | }
|
162 |
|
163 | logger.debug({
|
164 | label: 'debug',
|
165 | message: `config created ${JSON.stringify(next)}`,
|
166 | })
|
167 |
|
168 | return next
|
169 | }
|
170 |
|
\ | No newline at end of file |