@platformatic/generators 1.13.0

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.
@@ -0,0 +1,459 @@
1
+ 'use strict'
2
+
3
+ const { readFile, rm } = require('node:fs/promises')
4
+ const { test, afterEach, describe } = require('node:test')
5
+ const assert = require('node:assert')
6
+ const { join } = require('node:path')
7
+
8
+ const { fakeLogger, getTempDir } = require('./helpers')
9
+ const { BaseGenerator } = require('../lib/base-generator')
10
+ const { convertServiceNameToPrefix } = require('../lib/utils')
11
+
12
+ afterEach(async () => {
13
+ try {
14
+ await rm(join(__dirname, 'tmp'), { recursive: true })
15
+ } catch (err) {
16
+ // do nothing
17
+ }
18
+ })
19
+
20
+ test('should write file and dirs', async (t) => {
21
+ const dir = await getTempDir()
22
+ const gen = new BaseGenerator({
23
+ logger: fakeLogger
24
+ })
25
+
26
+ gen.setConfig({
27
+ targetDirectory: dir
28
+ })
29
+
30
+ await gen.run()
31
+ // check files are created
32
+ const packageJson = JSON.parse(await readFile(join(dir, 'package.json'), 'utf8'))
33
+ assert.ok(packageJson.scripts)
34
+ assert.ok(packageJson.dependencies)
35
+ assert.ok(packageJson.engines)
36
+
37
+ const configFile = JSON.parse(await readFile(join(dir, 'platformatic.json'), 'utf8'))
38
+ assert.deepEqual(configFile, {})
39
+
40
+ const gitignore = await readFile(join(dir, '.gitignore'), 'utf8')
41
+ assert.ok(gitignore.length > 0) // file is created and not empty
42
+ })
43
+
44
+ test('extended class should generate config', async (t) => {
45
+ class ServiceClass extends BaseGenerator {
46
+ async _getConfigFileContents () {
47
+ // Implement when extending this class
48
+ return {
49
+ foo: 'bar'
50
+ }
51
+ }
52
+ }
53
+
54
+ const svc = new ServiceClass({
55
+ type: 'service',
56
+ logger: fakeLogger
57
+ })
58
+
59
+ await svc.prepare()
60
+
61
+ const configFile = svc.files[1]
62
+ assert.deepEqual(configFile, {
63
+ path: '',
64
+ file: 'platformatic.service.json',
65
+ contents: JSON.stringify({ foo: 'bar' }, null, 2)
66
+ })
67
+ })
68
+
69
+ test('setConfig', async (t) => {
70
+ const bg = new BaseGenerator({
71
+ type: 'service'
72
+ })
73
+
74
+ // should init the default config
75
+ await bg.prepare()
76
+
77
+ assert.deepEqual(bg.config, {
78
+ port: 3042,
79
+ hostname: '0.0.0.0',
80
+ plugin: false,
81
+ typescript: false,
82
+ initGitRepository: false,
83
+ staticWorkspaceGitHubActions: false,
84
+ dynamicWorkspaceGitHubActions: false,
85
+ env: {},
86
+ dependencies: {},
87
+ devDependencies: {},
88
+ isRuntimeContext: false,
89
+ serviceName: '',
90
+ envPrefix: '',
91
+ tests: false
92
+ })
93
+
94
+ // should not have undefined properties
95
+ Object.entries(bg.config).forEach((kv) => {
96
+ assert.notStrictEqual(undefined, kv[1])
97
+ })
98
+
99
+ // partial config with defaults
100
+ bg.setConfig({
101
+ port: 3084
102
+ })
103
+
104
+ assert.deepEqual(bg.config, {
105
+ port: 3084, // this is the only custom value
106
+ hostname: '0.0.0.0',
107
+ plugin: false,
108
+ typescript: false,
109
+ initGitRepository: false,
110
+ staticWorkspaceGitHubActions: false,
111
+ dynamicWorkspaceGitHubActions: false,
112
+ env: {},
113
+ dependencies: {},
114
+ devDependencies: {},
115
+ isRuntimeContext: false,
116
+ serviceName: '',
117
+ envPrefix: '',
118
+ tests: false
119
+ })
120
+
121
+ // reset config to defaults
122
+ bg.setConfig()
123
+ assert.deepEqual(bg.config, {
124
+ port: 3042,
125
+ hostname: '0.0.0.0',
126
+ plugin: false,
127
+ typescript: false,
128
+ initGitRepository: false,
129
+ staticWorkspaceGitHubActions: false,
130
+ dynamicWorkspaceGitHubActions: false,
131
+ env: {},
132
+ dependencies: {},
133
+ devDependencies: {},
134
+ isRuntimeContext: false,
135
+ serviceName: '',
136
+ envPrefix: '',
137
+ tests: false
138
+ })
139
+ })
140
+
141
+ test('should append env values', async (t) => {
142
+ const bg = new BaseGenerator({
143
+ type: 'service'
144
+ })
145
+ // partial config with defaults
146
+ bg.setConfig({
147
+ env: {
148
+ FOO: 'bar'
149
+ }
150
+ })
151
+
152
+ await bg.prepare()
153
+ const dotEnvFile = bg.getFileObject('.env')
154
+ assert.equal(dotEnvFile.contents, 'FOO=bar\n')
155
+ })
156
+
157
+ test('should return service metadata', async (t) => {
158
+ const bg = new BaseGenerator({
159
+ type: 'service'
160
+ })
161
+ // partial config with defaults
162
+ bg.setConfig({
163
+ targetDirectory: '/foo/bar',
164
+ env: {
165
+ FOO: 'bar'
166
+ }
167
+ })
168
+
169
+ const metadata = await bg.prepare()
170
+ assert.deepEqual(metadata, {
171
+ targetDirectory: '/foo/bar',
172
+ env: {
173
+ FOO: 'bar'
174
+ }
175
+ })
176
+ })
177
+
178
+ test('should generate javascript plugin, routes and tests', async (t) => {
179
+ const bg = new BaseGenerator({
180
+ type: 'service'
181
+ })
182
+ bg.setConfig({
183
+ plugin: true,
184
+ tests: true
185
+ })
186
+ await bg.prepare()
187
+ assert.ok(bg.getFileObject('example.js', 'plugins'))
188
+ assert.ok(bg.getFileObject('root.js', 'routes'))
189
+
190
+ assert.ok(bg.getFileObject('root.test.js', 'test/routes'))
191
+ assert.ok(bg.getFileObject('example.test.js', 'test/plugins'))
192
+ })
193
+
194
+ test('should generate tsConfig file and typescript files', async (t) => {
195
+ const bg = new BaseGenerator({
196
+ type: 'service'
197
+ })
198
+ bg.setConfig({
199
+ typescript: true,
200
+ plugin: true,
201
+ tests: true
202
+ })
203
+ const template = {
204
+ compilerOptions: {
205
+ module: 'commonjs',
206
+ esModuleInterop: true,
207
+ target: 'es2020',
208
+ sourceMap: true,
209
+ pretty: true,
210
+ noEmitOnError: true,
211
+ incremental: true,
212
+ strict: true,
213
+ outDir: 'dist'
214
+ },
215
+ watchOptions: {
216
+ watchFile: 'fixedPollingInterval',
217
+ watchDirectory: 'fixedPollingInterval',
218
+ fallbackPolling: 'dynamicPriority',
219
+ synchronousWatchDirectory: true,
220
+ excludeDirectories: ['**/node_modules', 'dist']
221
+ }
222
+ }
223
+ await bg.prepare()
224
+ const tsConfigFile = bg.getFileObject('tsconfig.json')
225
+ assert.deepEqual(JSON.parse(tsConfigFile.contents), template)
226
+
227
+ assert.ok(bg.getFileObject('example.ts', 'plugins'))
228
+ assert.ok(bg.getFileObject('root.ts', 'routes'))
229
+
230
+ assert.ok(bg.getFileObject('root.test.ts', 'test/routes'))
231
+ assert.ok(bg.getFileObject('example.test.ts', 'test/plugins'))
232
+ })
233
+
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
+
294
+ const bg = new BaseGenerator({
295
+ type: 'service'
296
+ })
297
+
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
+ bg._beforePrepare = async () => {
328
+ throw new Error('beforePrepare error')
329
+ }
330
+ try {
331
+ await bg.prepare()
332
+ assert.fail()
333
+ } catch (err) {
334
+ assert.equal(err.code, 'PLT_GEN_PREPARE_ERROR')
335
+ assert.equal(err.message, 'Error while generating the files: beforePrepare error.')
336
+ }
337
+ })
338
+
339
+ test('should throw if there is a missing env variable', async () => {
340
+ const bg = new BaseGenerator()
341
+
342
+ bg._getConfigFileContents = async () => {
343
+ return {
344
+ FOO: '{FOO}',
345
+ BAR: '{BAR}'
346
+ }
347
+ }
348
+
349
+ bg.setConfig({
350
+ env: {
351
+ FOO: 'foobar'
352
+ }
353
+ })
354
+
355
+ try {
356
+ await bg.prepare()
357
+ assert.fail()
358
+ } catch (err) {
359
+ assert.equal(err.code, 'PLT_GEN_MISSING_ENV_VAR')
360
+ assert.equal(err.message, 'Env variable BAR is defined in config file platformatic.json, but not in config.env object.')
361
+ }
362
+ })
363
+
364
+ describe('runtime context', () => {
365
+ test('should set config.envPrefix correctly', async (t) => {
366
+ const bg = new BaseGenerator()
367
+
368
+ bg.setConfig({
369
+ isRuntimeContext: true,
370
+ serviceName: 'sample-service'
371
+ })
372
+
373
+ assert.equal(bg.config.envPrefix, 'SAMPLE_SERVICE')
374
+
375
+ bg.setConfig({
376
+ isRuntimeContext: true,
377
+ serviceName: 'sample-service',
378
+ envPrefix: 'ANOTHER_PREFIX',
379
+ env: {
380
+ FOO: 'bar',
381
+ BAZ: 'baz'
382
+ }
383
+ })
384
+
385
+ assert.equal(bg.config.envPrefix, 'ANOTHER_PREFIX')
386
+ assert.deepEqual(bg.config.env, {
387
+ PLT_ANOTHER_PREFIX_FOO: 'bar',
388
+ PLT_ANOTHER_PREFIX_BAZ: 'baz'
389
+ })
390
+ })
391
+
392
+ test('should generate correct env file from config.env', async (t) => {
393
+ const bg = new BaseGenerator()
394
+
395
+ bg.setConfig({
396
+ isRuntimeContext: true,
397
+ serviceName: 'sample-service',
398
+ envPrefix: 'ANOTHER_PREFIX',
399
+ env: {
400
+ FOO: 'bar',
401
+ BAZ: 'baz'
402
+ }
403
+ })
404
+
405
+ const meta = await bg.prepare()
406
+
407
+ assert.deepEqual(meta.env, {
408
+ PLT_ANOTHER_PREFIX_FOO: 'bar',
409
+ PLT_ANOTHER_PREFIX_BAZ: 'baz'
410
+ })
411
+ })
412
+
413
+ test('should return service metadata', async (t) => {
414
+ const bg = new BaseGenerator({
415
+ type: 'service'
416
+ })
417
+ // partial config with defaults
418
+ bg.setConfig({
419
+ targetDirectory: '/foo/bar',
420
+ isRuntimeContext: true,
421
+ serviceName: 'my-service',
422
+ env: {
423
+ FOO: 'bar'
424
+ }
425
+ })
426
+
427
+ const metadata = await bg.prepare()
428
+ assert.deepEqual(metadata, {
429
+ targetDirectory: '/foo/bar',
430
+ env: {
431
+ PLT_MY_SERVICE_FOO: 'bar'
432
+ }
433
+ })
434
+ })
435
+
436
+ test('should generate service name if not provided', async () => {
437
+ const bg = new BaseGenerator({
438
+ type: 'service'
439
+ })
440
+ bg.setConfig({
441
+ targetDirectory: '/foo/bar',
442
+ isRuntimeContext: true,
443
+ env: {
444
+ FOO: 'bar'
445
+ }
446
+ })
447
+
448
+ const metadata = await bg.prepare()
449
+
450
+ assert.equal(bg.config.envPrefix, convertServiceNameToPrefix(bg.config.serviceName))
451
+ const envPrefix = bg.config.envPrefix
452
+ assert.deepEqual(metadata, {
453
+ targetDirectory: '/foo/bar',
454
+ env: {
455
+ [`PLT_${envPrefix}_FOO`]: 'bar'
456
+ }
457
+ })
458
+ })
459
+ })
@@ -0,0 +1,106 @@
1
+ 'use strict'
2
+
3
+ const { test, describe } = require('node:test')
4
+ const assert = require('node:assert')
5
+ const { FileGenerator } = require('../lib/file-generator')
6
+ const { rm, readFile } = require('node:fs/promises')
7
+ const { join } = require('node:path')
8
+ const { tmpdir } = require('node:os')
9
+ const { safeMkdir } = require('../lib/utils')
10
+
11
+ let dirCount = 0
12
+ describe('FileGenerator', () => {
13
+ test('should return null if file is not found', async () => {
14
+ const fg = new FileGenerator()
15
+ const fileObject = fg.getFileObject('sample', 'file')
16
+ assert.strictEqual(null, fileObject)
17
+ })
18
+ test('should throw if no targeDirectory is set', async (t) => {
19
+ const fg = new FileGenerator()
20
+ assert.rejects(async () => {
21
+ await fg.writeFiles()
22
+ })
23
+ })
24
+
25
+ test('should replace a file with same name and path', async (t) => {
26
+ const fg = new FileGenerator()
27
+ fg.addFile({ path: 'path', file: 'file', contents: 'hello world' })
28
+ fg.addFile({ path: 'path', file: 'file', contents: 'foobar' })
29
+
30
+ const fileObject = fg.getFileObject('file', 'path')
31
+ assert.equal(fileObject.contents, 'foobar')
32
+ assert.equal(fg.files.length, 1)
33
+ })
34
+
35
+ test('should list files', async (t) => {
36
+ const fg = new FileGenerator()
37
+ fg.addFile({ path: 'path', file: 'helloworld.txt', contents: 'hello world' })
38
+ fg.addFile({ path: 'path', file: 'foobar.txt', contents: 'foobar' })
39
+ fg.addFile({ path: '/anotherpath', file: 'foobar.txt', contents: 'foobar' })
40
+
41
+ assert.deepEqual(fg.listFiles(), [
42
+ 'path/helloworld.txt',
43
+ 'path/foobar.txt',
44
+ 'anotherpath/foobar.txt'
45
+ ])
46
+ })
47
+ test('should append file content', async (t) => {
48
+ const fg = new FileGenerator()
49
+ fg.addFile({ path: 'path', file: 'helloworld.txt', contents: 'hello world' })
50
+
51
+ fg.appendfile({ path: '/path', file: 'helloworld.txt', contents: 'Welcome to plaftormatic' })
52
+
53
+ const fileObject = fg.getFileObject('helloworld.txt', 'path')
54
+ assert.equal(fileObject.contents, 'hello world\nWelcome to plaftormatic')
55
+
56
+ // new file
57
+ fg.appendfile({ path: '/path', file: 'foobar.txt', contents: 'foobar' })
58
+ const newFileObject = fg.getFileObject('foobar.txt', 'path')
59
+ assert.equal(newFileObject.contents, 'foobar')
60
+ })
61
+
62
+ test('should reset all files', async (t) => {
63
+ const fg = new FileGenerator()
64
+ fg.addFile({ path: 'path', file: 'file', contents: 'hello world' })
65
+ fg.addFile({ path: 'path', file: 'file', contents: 'foobar' })
66
+
67
+ fg.reset()
68
+ assert.equal(fg.files.length, 0)
69
+ })
70
+
71
+ test('should write files', async (t) => {
72
+ const tempDir = join(tmpdir(), `plt-file-generator-test-${dirCount++}`)
73
+ t.after(async () => {
74
+ await rm(tempDir, { recursive: true })
75
+ })
76
+
77
+ await safeMkdir(tempDir)
78
+ const fg = new FileGenerator()
79
+ fg.setTargetDirectory(tempDir)
80
+ fg.addFile({ path: 'myDir', file: 'helloworld.txt', contents: 'hello world' })
81
+
82
+ await fg.writeFiles()
83
+ const fileContents = await readFile(join(tempDir, 'myDir', 'helloworld.txt'), 'utf8')
84
+ assert.equal(fileContents, 'hello world')
85
+ })
86
+
87
+ test('should not write empty files', async (t) => {
88
+ const tempDir = join(tmpdir(), `plt-file-generator-test-${dirCount++}`)
89
+ t.after(async () => {
90
+ await rm(tempDir, { recursive: true })
91
+ })
92
+
93
+ await safeMkdir(tempDir)
94
+ const fg = new FileGenerator()
95
+ fg.setTargetDirectory(tempDir)
96
+ fg.addFile({ path: 'myDir', file: 'helloworld.txt', contents: '' })
97
+
98
+ await fg.writeFiles()
99
+ try {
100
+ await readFile(join(tempDir, 'myDir', 'helloworld.txt'), 'utf8')
101
+ assert.fail()
102
+ } catch (err) {
103
+ assert.equal(err.code, 'ENOENT')
104
+ }
105
+ })
106
+ })
@@ -0,0 +1,43 @@
1
+ 'use strict'
2
+
3
+ const { join } = require('node:path')
4
+ const fs = require('node:fs/promises')
5
+ const { safeMkdir } = require('../lib/utils')
6
+
7
+ let counter = 0
8
+
9
+ async function getTempDir (baseDir) {
10
+ if (baseDir === undefined) {
11
+ baseDir = __dirname
12
+ }
13
+ const dir = join(baseDir, 'tmp', `platformatic-generators-${process.pid}-${Date.now()}-${counter++}`)
14
+ await safeMkdir(dir)
15
+ return dir
16
+ }
17
+ async function moveToTmpdir (teardown) {
18
+ const cwd = process.cwd()
19
+ const tmp = join(__dirname, 'tmp')
20
+ try {
21
+ await fs.mkdir(tmp)
22
+ } catch {
23
+ }
24
+ const dir = await getTempDir(tmp)
25
+ await fs.mkdir(dir)
26
+ process.chdir(dir)
27
+ teardown(() => process.chdir(cwd))
28
+ if (!process.env.SKIP_RM_TMP) {
29
+ teardown(() => fs.rm(tmp, { recursive: true }).catch(() => {}))
30
+ }
31
+ return dir
32
+ }
33
+
34
+ module.exports = {
35
+ fakeLogger: {
36
+ info: () => {},
37
+ debug: () => {},
38
+ warn: () => {},
39
+ error: () => {}
40
+ },
41
+ getTempDir,
42
+ moveToTmpdir
43
+ }
package/test/runner.js ADDED
@@ -0,0 +1,20 @@
1
+ 'use strict'
2
+
3
+ const { tap, spec } = require('node:test/reporters')
4
+ const { run } = require('node:test')
5
+ const path = require('node:path')
6
+ const glob = require('glob').globSync
7
+
8
+ /* eslint-disable new-cap */
9
+ const reporter = process.stdout.isTTY ? new spec() : tap
10
+
11
+ const files = [
12
+ ...glob(path.join(__dirname, '*.test.js')),
13
+ ...glob(path.join(__dirname, 'cli', '*.test.mjs'))
14
+ ]
15
+
16
+ run({
17
+ files,
18
+ concurrency: 1,
19
+ timeout: 30000
20
+ }).compose(reporter).pipe(process.stdout)