UNPKG

3.85 kBJavaScriptView Raw
1
2const fs = require('./utils/fs')
3const path = require('path')
4
5exports.getSampleConfig = async () => {
6 return fs.readFile(path.resolve(__dirname, './templates/config.js'), 'utf8')
7}
8
9exports.findConfig = async () => {
10 const configName = 'exodus.config.js'
11 const targetConfig = await fs.findUpwardsFile(configName)
12 if (!targetConfig) {
13 const err = new Error(`Could not find ${configName} in this or any parent directories.`)
14 err.code = 'NOCONFIG'
15 throw err
16 }
17 return targetConfig
18}
19
20let _config
21let _context
22let _state
23exports.getConfig = async () => {
24 if (!_config) {
25 const targetConfig = await this.findConfig()
26
27 const externalConfig = require(targetConfig)
28 _config = {
29 ...defaultConfig,
30 ...externalConfig,
31 }
32 // Resolve the absolute path to the migrations directory
33 const configDir = path.dirname(targetConfig)
34 _config.migrationsDirectory = path.resolve(
35 configDir,
36 _config.migrationsDirectory
37 )
38
39 // If no statehandlers have been defined, fall back to filebased state.
40 if (!_config.storeState) {
41 const statePath = path.join(configDir, 'exodus.state.json')
42 _config.storeState = async (state) => {
43 return fs.writeFile(statePath, JSON.stringify(state, null, 2), 'utf8')
44 }
45 }
46 if (!_config.fetchState) {
47 _config.fetchState = async () => {
48 const statePath = path.join(configDir, 'exodus.state.json')
49 try {
50 return JSON.parse(await fs.readFile(statePath, 'utf8'))
51 } catch (err) {
52 if (err.code !== 'ENOENT') throw err
53 return {}
54 }
55 }
56 }
57
58 // Transform state into a singleton
59 const originalStateGetter = _config.fetchState
60 _config.fetchState = async (...args) => {
61 if (!_state) {
62 _state = await originalStateGetter(...args)
63 if (!_state) _state = {}
64 _state.history = _state.history || []
65 }
66 return _state
67 }
68
69 // Transform context into a singleton
70 const originalContextGetter = _config.context
71 _config.context = async (...args) => {
72 if (!_context) {
73 _context = await originalContextGetter(...args)
74 }
75 return _context
76 }
77 }
78 return _config
79}
80
81const defaultConfig = {
82 /**
83 * @name migrationsDirectory
84 *
85 * The folder to store migration scripts in,
86 * relative to your configuration file.
87 */
88 migrationsDirectory: './migrations',
89
90 /**
91 * @name context
92 *
93 * Invoked at the beginning of a run, this method can return
94 * an object with any details you want passed through to all
95 * migrations, such as database connections, loggers, etc.
96 *
97 * @return {object}
98 */
99 context: async () => {
100 return {}
101 },
102
103 /**
104 * @name storeState
105 *
106 * Called to persist current migration state. Use this to store
107 * the `state` argument in Redis, to disk, your database etc.
108 * If undefined, Exodus falls back to exodus.state.json
109 *
110 * @param state The state object to be stored.
111 * @param context The object you returned in `context`
112 */
113 // storeState: async (state, context) => {},
114
115 /**
116 * @name initialize
117 *
118 * This method is responsible for fetching the current
119 * migration state, persisted by `storeState`.
120 * If undefined, Exodus falls back to exodus.state.json
121 *
122 * @param context The object you returned in `context`
123 * @return {object}
124 */
125 // fetchState: async (context) => {},
126
127 // Callback executed right before all queued migrations are executed.
128 beforeAll: async (pendingMigrations) => {},
129
130 // Callback executed before each migration.
131 beforeEach: async (migrationJob) => {},
132
133 // Callback executed after each migration.
134 afterEach: async (migrationJob) => {},
135
136 // Callback executed right after all queued migrations are executed.
137 afterAll: async (pendingMigrations) => {},
138
139}