@platformatic/generators 1.13.1 → 1.13.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@ import { FileGenerator } from './file-generator'
3
3
  import { PackageConfiguration } from './utils'
4
4
  export namespace BaseGenerator {
5
5
  export type BaseGeneratorOptions = FileGenerator.FileGeneratorOptions & {
6
- type?: 'service' | 'db' | 'composer'
6
+ module: string
7
7
  inquirer?: object
8
8
  }
9
9
 
@@ -109,5 +109,7 @@ export namespace BaseGenerator {
109
109
  _getConfigFileContents(): Promise<JSONValue>
110
110
  _generateEnv(): Promise<void>
111
111
  appendConfigEnv(): Promise<void>
112
+
113
+ postInstallActions(): Promise<void>
112
114
  }
113
- }
115
+ }
@@ -5,8 +5,8 @@ const { stripVersion, convertServiceNameToPrefix, addPrefixToEnv, extractEnvVari
5
5
  const { join } = require('node:path')
6
6
  const { FileGenerator } = require('./file-generator')
7
7
  const { generateTests, generatePlugins } = require('./create-plugin')
8
+ const { PrepareError, MissingEnvVariable, ModuleNeeded } = require('./errors')
8
9
  const { generateGitignore } = require('./create-gitignore')
9
- const { NoQuestionsError, PrepareError, MissingEnvVariable } = require('./errors')
10
10
  const generateName = require('boring-name-generator')
11
11
  /* c8 ignore start */
12
12
  const fakeLogger = {
@@ -21,15 +21,18 @@ const fakeLogger = {
21
21
  class BaseGenerator extends FileGenerator {
22
22
  constructor (opts = {}) {
23
23
  super(opts)
24
- this.type = opts.type
25
24
  this.files = []
26
25
  this.logger = opts.logger || fakeLogger
27
26
  this.questions = []
28
27
  this.pkgData = null
29
- this.inquirer = null
28
+ this.inquirer = opts.inquirer || null
30
29
  this.targetDirectory = opts.targetDirectory || null
31
30
  this.config = this.getDefaultConfig()
32
31
  this.packages = []
32
+ this.module = opts.module
33
+ if (!this.module) {
34
+ throw ModuleNeeded()
35
+ }
33
36
  }
34
37
 
35
38
  getDefaultConfig () {
@@ -115,11 +118,13 @@ class BaseGenerator extends FileGenerator {
115
118
 
116
119
  /* c8 ignore start */
117
120
  async ask () {
118
- if (!this.questions.length) {
119
- throw new NoQuestionsError()
120
- }
121
121
  if (this.inquirer) {
122
- this.config = await this.inquirer.prompt(this.questions)
122
+ await this.prepareQuestions()
123
+ const newConfig = await this.inquirer.prompt(this.questions)
124
+ this.setConfig({
125
+ ...this.config,
126
+ ...newConfig
127
+ })
123
128
  }
124
129
  }
125
130
 
@@ -154,8 +159,6 @@ class BaseGenerator extends FileGenerator {
154
159
 
155
160
  await this.generateConfigFile()
156
161
 
157
- await this.prepareQuestions()
158
-
159
162
  await this.generateEnv()
160
163
 
161
164
  if (this.config.typescript) {
@@ -172,12 +175,16 @@ class BaseGenerator extends FileGenerator {
172
175
  this.files.push(...generatePlugins(this.config.typescript))
173
176
  if (this.config.tests) {
174
177
  // create tests
175
- this.files.push(...generateTests(this.config.typescript, this.type))
178
+ this.files.push(...generateTests(this.config.typescript, this.module))
176
179
  }
177
180
  }
181
+
178
182
  this.files.push(generateGitignore())
183
+
179
184
  await this._afterPrepare()
185
+
180
186
  this.checkEnvVariablesInConfigFile()
187
+
181
188
  return {
182
189
  targetDirectory: this.targetDirectory,
183
190
  env: this.config.env
@@ -187,12 +194,14 @@ class BaseGenerator extends FileGenerator {
187
194
  // throw the same error
188
195
  throw err
189
196
  }
190
- throw new PrepareError(err.message)
197
+ const _err = new PrepareError(err.message)
198
+ _err.cause = err
199
+ throw _err
191
200
  }
192
201
  }
193
202
 
194
203
  checkEnvVariablesInConfigFile () {
195
- const configFileName = this.getConfigFileName()
204
+ const configFileName = 'platformatic.json'
196
205
  const fileOjbect = this.getFileObject(configFileName)
197
206
  const envVars = extractEnvVariablesFromText(fileOjbect.contents)
198
207
  const envKeys = Object.keys(this.config.env)
@@ -231,113 +240,36 @@ class BaseGenerator extends FileGenerator {
231
240
  }
232
241
 
233
242
  async prepareQuestions () {
234
- // directory
235
- this.questions.push({
236
- type: 'input',
237
- name: 'targetDirectory',
238
- message: 'Where would you like to create your project?'
239
- })
240
-
241
- // typescript
242
- this.questions.push({
243
- type: 'list',
244
- name: 'typescript',
245
- message: 'Do you want to use TypeScript?',
246
- default: false,
247
- choices: [{ name: 'yes', value: true }, { name: 'no', value: false }]
248
- })
249
-
250
- // init git repository
251
- this.questions.push({
252
- type: 'list',
253
- name: 'initGitRepository',
254
- message: 'Do you want to init the git repository?',
255
- default: false,
256
- choices: [{ name: 'yes', value: true }, { name: 'no', value: false }]
257
- })
258
-
259
- // port
260
- this.questions.push({
261
- type: 'input',
262
- name: 'port',
263
- message: 'What port do you want to use?'
264
- })
265
-
266
- // github actions
267
- this.questions.push({
268
- type: 'list',
269
- name: 'staticWorkspaceGitHubAction',
270
- message: 'Do you want to create the github action to deploy this application to Platformatic Cloud?',
271
- default: true,
272
- choices: [{ name: 'yes', value: true }, { name: 'no', value: false }]
273
- },
274
- {
275
- type: 'list',
276
- name: 'dynamicWorkspaceGitHubAction',
277
- message: 'Do you want to enable PR Previews in your application?',
278
- default: true,
279
- choices: [{ name: 'yes', value: true }, { name: 'no', value: false }]
280
- })
281
- }
282
-
283
- async addQuestion (question, where) {
284
- if (where) {
285
- if (where.before) {
286
- const position = this.questions.reduce((acc, element, idx) => {
287
- if (acc === null) {
288
- if (element.name === where.before) {
289
- acc = idx
290
- }
291
- }
292
- return acc
293
- }, null)
294
-
295
- if (position) {
296
- this.questions.splice(position, 0, question)
297
- }
298
- } else if (where.after) {
299
- const position = this.questions.reduce((acc, element, idx) => {
300
- if (acc === null) {
301
- if (element.name === where.after) {
302
- acc = idx + 1
303
- }
304
- }
305
- return acc
306
- }, null)
307
-
308
- if (position) {
309
- this.questions.splice(position, 0, question)
310
- }
243
+ if (!this.config.isRuntimeContext) {
244
+ if (!this.config.targetDirectory) {
245
+ // directory
246
+ this.questions.push({
247
+ type: 'input',
248
+ name: 'targetDirectory',
249
+ message: 'Where would you like to create your project?'
250
+ })
311
251
  }
312
- } else {
313
- this.questions.push(question)
314
- }
315
- }
316
252
 
317
- removeQuestion (variableName) {
318
- const position = this.questions.reduce((acc, element, idx) => {
319
- if (acc === null) {
320
- if (element.name === variableName) {
321
- acc = idx
322
- }
323
- }
324
- return acc
325
- }, null)
326
- if (position) {
327
- this.questions.splice(position, 1)
328
- }
329
- }
253
+ // typescript
254
+ this.questions.push({
255
+ type: 'list',
256
+ name: 'typescript',
257
+ message: 'Do you want to use TypeScript?',
258
+ default: false,
259
+ choices: [{ name: 'yes', value: true }, { name: 'no', value: false }]
260
+ })
330
261
 
331
- getConfigFileName () {
332
- if (!this.type) {
333
- return 'platformatic.json'
334
- } else {
335
- return `platformatic.${this.type}.json`
262
+ // port
263
+ this.questions.push({
264
+ type: 'input',
265
+ name: 'port',
266
+ message: 'What port do you want to use?'
267
+ })
336
268
  }
337
269
  }
338
270
 
339
271
  async generateConfigFile () {
340
- const configFileName = this.getConfigFileName()
272
+ const configFileName = 'platformatic.json'
341
273
  const contents = await this._getConfigFileContents()
342
274
  this.addFile({
343
275
  path: '',
@@ -407,6 +339,13 @@ class BaseGenerator extends FileGenerator {
407
339
  })
408
340
  await this._generateEnv()
409
341
  this.appendConfigEnv()
342
+
343
+ const { contents } = this.getFileObject('.env')
344
+ this.addFile({
345
+ path: '',
346
+ file: '.env.sample',
347
+ contents
348
+ })
410
349
  }
411
350
  }
412
351
 
@@ -421,6 +360,8 @@ class BaseGenerator extends FileGenerator {
421
360
  }
422
361
 
423
362
  // implement in the subclass
363
+ /* c8 ignore next 1 */
364
+ async postInstallActions () {}
424
365
  async _beforePrepare () {}
425
366
  async _afterPrepare () {}
426
367
  async _getConfigFileContents () { return {} }
@@ -115,7 +115,7 @@ function testHelperTS (mod, customizations = { pre: '', post: '', config: '', re
115
115
  return `\
116
116
  import { join } from 'node:path'
117
117
  import { readFile } from 'node:fs/promises'
118
- import { buildServer } from '@platformatic/${mod}'
118
+ import { buildServer } from '${mod}'
119
119
  import { test } from 'node:test'
120
120
  ${customizations.requires}
121
121
 
@@ -125,7 +125,7 @@ type TestContext = Parameters<Exclude<testfn, undefined>>[0]
125
125
  export async function getServer (t: TestContext) {
126
126
  ${customizations.pre}
127
127
  // We go up two folder because this files executes in the dist folder
128
- const config = JSON.parse(await readFile(join(__dirname, '..', '..', 'platformatic.${mod}.json'), 'utf8'))
128
+ const config = JSON.parse(await readFile(join(__dirname, '..', '..', 'platformatic'), 'utf8'))
129
129
  // Add your config customizations here. For example you want to set
130
130
  // all things that are set in the config file to read from an env variable
131
131
  config.server.logger.level = 'warn'
@@ -199,13 +199,13 @@ function generateRouteWithTypesSupport (typescript) {
199
199
  }
200
200
  }
201
201
 
202
- function generateTests (typescript, type, customizations) {
202
+ function generateTests (typescript, mod, customizations) {
203
203
  const output = []
204
204
  if (typescript) {
205
205
  output.push({
206
206
  path: 'test',
207
207
  file: 'helper.ts',
208
- contents: testHelperTS(type, customizations)
208
+ contents: testHelperTS(mod, customizations)
209
209
  })
210
210
  output.push({
211
211
  path: join('test', 'plugins'),
@@ -221,7 +221,7 @@ function generateTests (typescript, type, customizations) {
221
221
  output.push({
222
222
  path: 'test',
223
223
  file: 'helper.js',
224
- contents: testHelperJS(type, customizations)
224
+ contents: testHelperJS(mod, customizations)
225
225
  })
226
226
  output.push({
227
227
  path: join('test', 'plugins'),
package/lib/errors.js CHANGED
@@ -5,7 +5,7 @@ const createError = require('@fastify/error')
5
5
  const ERROR_PREFIX = 'PLT_GEN'
6
6
 
7
7
  module.exports = {
8
- NoQuestionsError: createError(`${ERROR_PREFIX}_NO_QUESTIONS_ERROR`, 'No questions added.'),
8
+ ModuleNeeded: createError(`${ERROR_PREFIX}_PREPARE_ERROR`, 'The module which the package will be published to must be specified'),
9
9
  PrepareError: createError(`${ERROR_PREFIX}_PREPARE_ERROR`, 'Error while generating the files: %s.'),
10
10
  MissingEnvVariable: createError(`${ERROR_PREFIX}_MISSING_ENV_VAR`, 'Env variable %s is defined in config file %s, but not in config.env object.'),
11
11
  WrongTypeError: createError(`${ERROR_PREFIX}_WRONG_TYPE`, 'Invalid value type. Accepted values are \'string\', \'number\' and \'boolean\', found \'%s\'.')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/generators",
3
- "version": "1.13.1",
3
+ "version": "1.13.3",
4
4
  "description": "Main classes and utils for generators.",
5
5
  "main": "index.js",
6
6
  "keywords": [],
@@ -20,7 +20,8 @@ afterEach(async () => {
20
20
  test('should write file and dirs', async (t) => {
21
21
  const dir = await getTempDir()
22
22
  const gen = new BaseGenerator({
23
- logger: fakeLogger
23
+ logger: fakeLogger,
24
+ module: '@platformatic/service'
24
25
  })
25
26
 
26
27
  gen.setConfig({
@@ -43,6 +44,13 @@ test('should write file and dirs', async (t) => {
43
44
 
44
45
  test('extended class should generate config', async (t) => {
45
46
  class ServiceClass extends BaseGenerator {
47
+ constructor (opts) {
48
+ super({
49
+ ...opts,
50
+ module: '@platformatic/service'
51
+ })
52
+ }
53
+
46
54
  async _getConfigFileContents () {
47
55
  // Implement when extending this class
48
56
  return {
@@ -52,7 +60,6 @@ test('extended class should generate config', async (t) => {
52
60
  }
53
61
 
54
62
  const svc = new ServiceClass({
55
- type: 'service',
56
63
  logger: fakeLogger
57
64
  })
58
65
 
@@ -61,14 +68,14 @@ test('extended class should generate config', async (t) => {
61
68
  const configFile = svc.files[1]
62
69
  assert.deepEqual(configFile, {
63
70
  path: '',
64
- file: 'platformatic.service.json',
71
+ file: 'platformatic.json',
65
72
  contents: JSON.stringify({ foo: 'bar' }, null, 2)
66
73
  })
67
74
  })
68
75
 
69
76
  test('setConfig', async (t) => {
70
77
  const bg = new BaseGenerator({
71
- type: 'service'
78
+ module: '@platformatic/service'
72
79
  })
73
80
 
74
81
  // should init the default config
@@ -140,7 +147,7 @@ test('setConfig', async (t) => {
140
147
 
141
148
  test('should append env values', async (t) => {
142
149
  const bg = new BaseGenerator({
143
- type: 'service'
150
+ module: '@platformatic/service'
144
151
  })
145
152
  // partial config with defaults
146
153
  bg.setConfig({
@@ -152,11 +159,85 @@ test('should append env values', async (t) => {
152
159
  await bg.prepare()
153
160
  const dotEnvFile = bg.getFileObject('.env')
154
161
  assert.equal(dotEnvFile.contents, 'FOO=bar\n')
162
+
163
+ const dotEnvSampleFile = bg.getFileObject('.env.sample')
164
+ assert.equal(dotEnvSampleFile.contents, 'FOO=bar\n')
165
+ })
166
+
167
+ test('should prepare the questions', async (t) => {
168
+ const bg = new BaseGenerator({
169
+ module: '@platformatic/service'
170
+ })
171
+ // partial config with defaults
172
+ bg.setConfig({
173
+ env: {
174
+ FOO: 'bar'
175
+ }
176
+ })
177
+
178
+ await bg.prepareQuestions()
179
+ assert.deepStrictEqual(bg.questions, [{
180
+ type: 'input',
181
+ name: 'targetDirectory',
182
+ message: 'Where would you like to create your project?'
183
+ }, {
184
+ type: 'list',
185
+ name: 'typescript',
186
+ message: 'Do you want to use TypeScript?',
187
+ default: false,
188
+ choices: [{ name: 'yes', value: true }, { name: 'no', value: false }]
189
+ }, {
190
+ type: 'input',
191
+ name: 'port',
192
+ message: 'What port do you want to use?'
193
+ }])
194
+ })
195
+
196
+ test('should prepare the questions with a targetDirectory', async (t) => {
197
+ const bg = new BaseGenerator({
198
+ module: '@platformatic/service'
199
+ })
200
+ // partial config with defaults
201
+ bg.setConfig({
202
+ targetDirectory: './foo',
203
+ env: {
204
+ FOO: 'bar'
205
+ }
206
+ })
207
+
208
+ await bg.prepareQuestions()
209
+ assert.deepStrictEqual(bg.questions, [{
210
+ type: 'list',
211
+ name: 'typescript',
212
+ message: 'Do you want to use TypeScript?',
213
+ default: false,
214
+ choices: [{ name: 'yes', value: true }, { name: 'no', value: false }]
215
+ }, {
216
+ type: 'input',
217
+ name: 'port',
218
+ message: 'What port do you want to use?'
219
+ }])
220
+ })
221
+
222
+ test('should prepare the questions in runtime context', async (t) => {
223
+ const bg = new BaseGenerator({
224
+ module: '@platformatic/service'
225
+ })
226
+ // partial config with defaults
227
+ bg.setConfig({
228
+ isRuntimeContext: true,
229
+ env: {
230
+ FOO: 'bar'
231
+ }
232
+ })
233
+
234
+ await bg.prepareQuestions()
235
+ assert.deepStrictEqual(bg.questions, [])
155
236
  })
156
237
 
157
238
  test('should return service metadata', async (t) => {
158
239
  const bg = new BaseGenerator({
159
- type: 'service'
240
+ module: '@platformatic/service'
160
241
  })
161
242
  // partial config with defaults
162
243
  bg.setConfig({
@@ -177,7 +258,7 @@ test('should return service metadata', async (t) => {
177
258
 
178
259
  test('should generate javascript plugin, routes and tests', async (t) => {
179
260
  const bg = new BaseGenerator({
180
- type: 'service'
261
+ module: '@platformatic/service'
181
262
  })
182
263
  bg.setConfig({
183
264
  plugin: true,
@@ -193,7 +274,7 @@ test('should generate javascript plugin, routes and tests', async (t) => {
193
274
 
194
275
  test('should generate tsConfig file and typescript files', async (t) => {
195
276
  const bg = new BaseGenerator({
196
- type: 'service'
277
+ module: '@platformatic/service'
197
278
  })
198
279
  bg.setConfig({
199
280
  typescript: true,
@@ -231,99 +312,11 @@ test('should generate tsConfig file and typescript files', async (t) => {
231
312
  assert.ok(bg.getFileObject('example.test.ts', 'test/plugins'))
232
313
  })
233
314
 
234
- test('should generate platformatic.json file if no type is generated', async (t) => {
235
- const bg = new BaseGenerator()
236
- await bg.prepare()
237
- const configFileObject = bg.getFileObject('platformatic.json')
238
- assert.ok(configFileObject)
239
- })
240
-
241
- test('should add questions in the correct position (before)', async (t) => {
242
- const question = {
243
- type: 'input',
244
- name: 'serviceName',
245
- message: 'What is the name of the service?'
246
- }
247
-
248
- const bg = new BaseGenerator({
249
- type: 'service'
250
- })
251
-
252
- await bg.prepare()
253
- const originalQuestionsLength = bg.questions.length
254
- bg.addQuestion(question, { before: 'typescript' }) // should add as second question
255
-
256
- assert.equal(bg.questions.length, originalQuestionsLength + 1)
257
- assert.deepEqual(bg.questions[1], {
258
- type: 'input',
259
- name: 'serviceName',
260
- message: 'What is the name of the service?'
261
- })
262
- })
263
-
264
- test('should add questions in the correct position (after)', async (t) => {
265
- const question = {
266
- type: 'input',
267
- name: 'serviceName',
268
- message: 'What is the name of the service?'
269
- }
270
-
271
- const bg = new BaseGenerator({
272
- type: 'service'
273
- })
274
-
275
- await bg.prepare()
276
- const originalQuestionsLength = bg.questions.length
277
- bg.addQuestion(question, { after: 'typescript' }) // should add as third question
278
-
279
- assert.equal(bg.questions.length, originalQuestionsLength + 1)
280
- assert.deepEqual(bg.questions[2], {
281
- type: 'input',
282
- name: 'serviceName',
283
- message: 'What is the name of the service?'
284
- })
285
- })
286
-
287
- test('should add questions at the end', async (t) => {
288
- const question = {
289
- type: 'input',
290
- name: 'serviceName',
291
- message: 'What is the name of the service?'
292
- }
293
-
315
+ test('should throw if preapare fails', async (t) => {
294
316
  const bg = new BaseGenerator({
295
- type: 'service'
317
+ module: '@platformatic/service'
296
318
  })
297
319
 
298
- await bg.prepare()
299
- const originalQuestionsLength = bg.questions.length
300
- bg.addQuestion(question)
301
-
302
- assert.equal(bg.questions.length, originalQuestionsLength + 1)
303
- assert.deepEqual(bg.questions[originalQuestionsLength], {
304
- type: 'input',
305
- name: 'serviceName',
306
- message: 'What is the name of the service?'
307
- })
308
- })
309
-
310
- test('should remove question', async (t) => {
311
- const bg = new BaseGenerator()
312
-
313
- await bg.prepare()
314
-
315
- bg.removeQuestion('typescript')
316
-
317
- bg.questions.forEach((question) => {
318
- if (question.name === 'typescript') {
319
- assert.fail()
320
- }
321
- })
322
- })
323
-
324
- test('should throw if preapare fails', async (t) => {
325
- const bg = new BaseGenerator()
326
-
327
320
  bg._beforePrepare = async () => {
328
321
  throw new Error('beforePrepare error')
329
322
  }
@@ -337,7 +330,9 @@ test('should throw if preapare fails', async (t) => {
337
330
  })
338
331
 
339
332
  test('should throw if there is a missing env variable', async () => {
340
- const bg = new BaseGenerator()
333
+ const bg = new BaseGenerator({
334
+ module: '@platformatic/service'
335
+ })
341
336
 
342
337
  bg._getConfigFileContents = async () => {
343
338
  return {
@@ -362,7 +357,9 @@ test('should throw if there is a missing env variable', async () => {
362
357
  })
363
358
 
364
359
  test('should add package', async () => {
365
- const bg = new BaseGenerator()
360
+ const bg = new BaseGenerator({
361
+ module: '@platformatic/service'
362
+ })
366
363
 
367
364
  const packageDefinition = {
368
365
  name: '@my/package',
@@ -382,7 +379,9 @@ test('should add package', async () => {
382
379
 
383
380
  describe('runtime context', () => {
384
381
  test('should set config.envPrefix correctly', async (t) => {
385
- const bg = new BaseGenerator()
382
+ const bg = new BaseGenerator({
383
+ module: '@platformatic/service'
384
+ })
386
385
 
387
386
  bg.setConfig({
388
387
  isRuntimeContext: true,
@@ -409,7 +408,9 @@ describe('runtime context', () => {
409
408
  })
410
409
 
411
410
  test('should generate correct env file from config.env', async (t) => {
412
- const bg = new BaseGenerator()
411
+ const bg = new BaseGenerator({
412
+ module: '@platformatic/service'
413
+ })
413
414
 
414
415
  bg.setConfig({
415
416
  isRuntimeContext: true,
@@ -431,7 +432,7 @@ describe('runtime context', () => {
431
432
 
432
433
  test('should return service metadata', async (t) => {
433
434
  const bg = new BaseGenerator({
434
- type: 'service'
435
+ module: '@platformatic/service'
435
436
  })
436
437
  // partial config with defaults
437
438
  bg.setConfig({
@@ -454,7 +455,7 @@ describe('runtime context', () => {
454
455
 
455
456
  test('should generate service name if not provided', async () => {
456
457
  const bg = new BaseGenerator({
457
- type: 'service'
458
+ module: '@platformatic/service'
458
459
  })
459
460
  bg.setConfig({
460
461
  targetDirectory: '/foo/bar',