@platformatic/generators 2.67.0-alpha.2 → 2.67.1

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.
package/index.js CHANGED
@@ -1,10 +1,13 @@
1
1
  'use strict'
2
2
 
3
3
  const { BaseGenerator } = require('./lib/base-generator')
4
+ const { ImportGenerator } = require('./lib/import-generator')
4
5
  const { generateTests } = require('./lib/create-plugin')
5
6
  const utils = require('./lib/utils')
7
+
6
8
  module.exports = {
7
9
  BaseGenerator,
10
+ ImportGenerator,
8
11
  generateTests,
9
- ...utils,
12
+ ...utils
10
13
  }
@@ -8,7 +8,7 @@ const {
8
8
  getPackageConfigurationObject,
9
9
  PLT_ROOT,
10
10
  getLatestNpmVersion,
11
- stripVersion,
11
+ stripVersion
12
12
  } = require('./utils')
13
13
  const { join } = require('node:path')
14
14
  const { FileGenerator } = require('./file-generator')
@@ -20,11 +20,11 @@ const { flattenObject } = require('./utils')
20
20
  const { envStringToObject } = require('./utils')
21
21
  /* c8 ignore start */
22
22
  const fakeLogger = {
23
- info: () => { },
24
- warn: () => { },
25
- debug: () => { },
26
- trace: () => { },
27
- error: () => { },
23
+ info: () => {},
24
+ warn: () => {},
25
+ debug: () => {},
26
+ trace: () => {},
27
+ error: () => {}
28
28
  }
29
29
  /* c8 ignore start */
30
30
 
@@ -61,7 +61,7 @@ class BaseGenerator extends FileGenerator {
61
61
  envPrefix: '',
62
62
  env: {},
63
63
  defaultEnv: {},
64
- isUpdating: false,
64
+ isUpdating: false
65
65
  }
66
66
  }
67
67
 
@@ -72,9 +72,11 @@ class BaseGenerator extends FileGenerator {
72
72
  setConfigFields (fields) {
73
73
  const availableConfigFields = this.getConfigFieldsDefinitions()
74
74
  function shouldHandleConfigField (field) {
75
- return availableConfigFields.filter((f) => {
76
- return f.configValue === field.configValue && f.var === field.var
77
- }).length > 0
75
+ return (
76
+ availableConfigFields.filter(f => {
77
+ return f.configValue === field.configValue && f.var === field.var
78
+ }).length > 0
79
+ )
78
80
  }
79
81
  for (const field of fields) {
80
82
  if (shouldHandleConfigField(field)) {
@@ -138,7 +140,7 @@ class BaseGenerator extends FileGenerator {
138
140
  this.config = {
139
141
  ...this.getDefaultConfig(),
140
142
  ...oldConfig,
141
- ...config,
143
+ ...config
142
144
  }
143
145
 
144
146
  if (this.config.isRuntimeContext) {
@@ -164,7 +166,7 @@ class BaseGenerator extends FileGenerator {
164
166
  const newConfig = await this.inquirer.prompt(this.questions)
165
167
  this.setConfig({
166
168
  ...this.config,
167
- ...newConfig,
169
+ ...newConfig
168
170
  })
169
171
  }
170
172
  }
@@ -191,7 +193,7 @@ class BaseGenerator extends FileGenerator {
191
193
  this.addFile({
192
194
  path: '',
193
195
  file: this.runtimeConfig,
194
- contents: JSON.stringify(currentConfigFile, null, 2),
196
+ contents: JSON.stringify(currentConfigFile, null, 2)
195
197
  })
196
198
  } else {
197
199
  await this.getFastifyVersion()
@@ -204,7 +206,7 @@ class BaseGenerator extends FileGenerator {
204
206
  this.addFile({
205
207
  path: '',
206
208
  file: 'package.json',
207
- contents: JSON.stringify(template, null, 2),
209
+ contents: JSON.stringify(template, null, 2)
208
210
  })
209
211
 
210
212
  await this.generateConfigFile()
@@ -216,7 +218,7 @@ class BaseGenerator extends FileGenerator {
216
218
  this.addFile({
217
219
  path: '',
218
220
  file: 'tsconfig.json',
219
- contents: JSON.stringify(this.getTsConfig(), null, 2),
221
+ contents: JSON.stringify(this.getTsConfig(), null, 2)
220
222
  })
221
223
  }
222
224
 
@@ -237,7 +239,7 @@ class BaseGenerator extends FileGenerator {
237
239
  }
238
240
  return {
239
241
  targetDirectory: this.targetDirectory,
240
- env: this.config.env,
242
+ env: this.config.env
241
243
  }
242
244
  } catch (err) {
243
245
  if (err.code?.startsWith('PLT_GEN')) {
@@ -282,15 +284,15 @@ class BaseGenerator extends FileGenerator {
282
284
  incremental: true,
283
285
  strict: true,
284
286
  outDir: 'dist',
285
- skipLibCheck: true,
287
+ skipLibCheck: true
286
288
  },
287
289
  watchOptions: {
288
290
  watchFile: 'fixedPollingInterval',
289
291
  watchDirectory: 'fixedPollingInterval',
290
292
  fallbackPolling: 'dynamicPriority',
291
293
  synchronousWatchDirectory: true,
292
- excludeDirectories: ['**/node_modules', 'dist'],
293
- },
294
+ excludeDirectories: ['**/node_modules', 'dist']
295
+ }
294
296
  }
295
297
  }
296
298
 
@@ -301,7 +303,7 @@ class BaseGenerator extends FileGenerator {
301
303
  this.questions.push({
302
304
  type: 'input',
303
305
  name: 'targetDirectory',
304
- message: 'Where would you like to create your project?',
306
+ message: 'Where would you like to create your project?'
305
307
  })
306
308
  }
307
309
 
@@ -337,18 +339,18 @@ class BaseGenerator extends FileGenerator {
337
339
  if (!contents.plugins) {
338
340
  contents.plugins = {}
339
341
  }
340
- contents.plugins.packages = this.packages.map((packageDefinition) => {
342
+ contents.plugins.packages = this.packages.map(packageDefinition => {
341
343
  const packageConfigOutput = getPackageConfigurationObject(packageDefinition.options, this.config.serviceName)
342
344
  if (Object.keys(packageConfigOutput.env).length > 0) {
343
345
  const envForPackages = {}
344
- Object.entries(packageConfigOutput.env).forEach((kv) => {
346
+ Object.entries(packageConfigOutput.env).forEach(kv => {
345
347
  envForPackages[kv[0]] = kv[1]
346
348
  })
347
349
  this.addEnvVars(envForPackages)
348
350
  }
349
351
  return {
350
352
  name: packageDefinition.name,
351
- options: packageConfigOutput.config,
353
+ options: packageConfigOutput.config
352
354
  }
353
355
  })
354
356
  }
@@ -356,8 +358,10 @@ class BaseGenerator extends FileGenerator {
356
358
  this.addFile({
357
359
  path: '',
358
360
  file: configFileName,
359
- contents: JSON.stringify(contents, null, 2),
361
+ contents: JSON.stringify(contents, null, 2)
360
362
  })
363
+
364
+ return contents
361
365
  }
362
366
 
363
367
  /**
@@ -388,23 +392,24 @@ class BaseGenerator extends FileGenerator {
388
392
  name: `${this.config.serviceName}`,
389
393
  scripts: {
390
394
  start: 'platformatic start',
391
- test: 'borp',
395
+ test: 'borp'
392
396
  },
393
397
  devDependencies: {
394
398
  fastify: `^${this.fastifyVersion}`,
395
399
  borp: `${this.pkgData.devDependencies.borp}`,
396
- ...this.config.devDependencies,
400
+ ...this.config.devDependencies
397
401
  },
398
402
  dependencies: {
399
- ...this.config.dependencies,
403
+ ...this.config.dependencies
400
404
  },
401
405
  engines: {
402
- node: '^22.14.0 || ^20.6.0',
403
- },
406
+ node: '^22.14.0 || ^20.6.0'
407
+ }
404
408
  }
405
409
 
406
410
  if (this.config.typescript) {
407
- const typescriptVersion = JSON.parse(await readFile(join(__dirname, '..', 'package.json'), 'utf-8')).devDependencies.typescript
411
+ const typescriptVersion = JSON.parse(await readFile(join(__dirname, '..', 'package.json'), 'utf-8'))
412
+ .devDependencies.typescript
408
413
  template.scripts.clean = 'rm -fr ./dist'
409
414
  template.scripts.build = 'platformatic compile'
410
415
  template.devDependencies.typescript = typescriptVersion
@@ -413,29 +418,35 @@ class BaseGenerator extends FileGenerator {
413
418
  }
414
419
 
415
420
  async generateEnv () {
416
- if (!this.config.isRuntimeContext) {
417
- this.addFile({
418
- path: '',
419
- file: '.env',
420
- contents: serializeEnvVars(this.config.env),
421
- })
421
+ if (this.config.isRuntimeContext) {
422
+ return
423
+ }
422
424
 
423
- const emptyEnvVars = {}
424
- for (const envVarName of Object.keys(this.config.env)) {
425
- if (!this.config.defaultEnv[envVarName]) {
426
- emptyEnvVars[envVarName] = ''
427
- }
425
+ const serializedEnv = serializeEnvVars(this.config.env)
426
+
427
+ this.addFile({
428
+ path: '',
429
+ file: '.env',
430
+ contents: serializedEnv
431
+ })
432
+
433
+ const emptyEnvVars = {}
434
+ for (const envVarName of Object.keys(this.config.env)) {
435
+ if (!this.config.defaultEnv[envVarName]) {
436
+ emptyEnvVars[envVarName] = ''
428
437
  }
438
+ }
429
439
 
430
- this.addFile({
431
- path: '',
432
- file: '.env.sample',
433
- contents: serializeEnvVars({
434
- ...this.config.defaultEnv,
435
- ...emptyEnvVars,
436
- }),
440
+ this.addFile({
441
+ path: '',
442
+ file: '.env.sample',
443
+ contents: serializeEnvVars({
444
+ ...this.config.defaultEnv,
445
+ ...emptyEnvVars
437
446
  })
438
- }
447
+ })
448
+
449
+ return serializedEnv
439
450
  }
440
451
 
441
452
  async run () {
@@ -460,7 +471,9 @@ class BaseGenerator extends FileGenerator {
460
471
  async loadFromDir (serviceName, runtimeRootPath) {
461
472
  const runtimePkgConfigFileData = JSON.parse(await readFile(join(runtimeRootPath, this.runtimeConfig), 'utf-8'))
462
473
  const servicesPath = runtimePkgConfigFileData.autoload.path
463
- const servicePkgJsonFileData = JSON.parse(await readFile(join(runtimeRootPath, servicesPath, serviceName, 'platformatic.json'), 'utf-8'))
474
+ const servicePkgJsonFileData = JSON.parse(
475
+ await readFile(join(runtimeRootPath, servicesPath, serviceName, 'platformatic.json'), 'utf-8')
476
+ )
464
477
  const runtimeEnv = envStringToObject(await readFile(join(runtimeRootPath, '.env'), 'utf-8'))
465
478
  const serviceNamePrefix = convertServiceNameToPrefix(serviceName)
466
479
  const plugins = []
@@ -469,7 +482,7 @@ class BaseGenerator extends FileGenerator {
469
482
  const flattened = flattenObject(pkg)
470
483
  const output = {
471
484
  name: flattened.name,
472
- options: [],
485
+ options: []
473
486
  }
474
487
  if (pkg.options) {
475
488
  Object.entries(flattened)
@@ -481,7 +494,7 @@ class BaseGenerator extends FileGenerator {
481
494
  name: serviceEnvVarKey,
482
495
  path: key.replace('options.', ''),
483
496
  type: 'string',
484
- value: runtimeEnv[runtimeEnvVarKey],
497
+ value: runtimeEnv[runtimeEnvVarKey]
485
498
  }
486
499
  output.options.push(option)
487
500
  })
@@ -495,16 +508,18 @@ class BaseGenerator extends FileGenerator {
495
508
  name: serviceName,
496
509
  template: getServiceTemplateFromSchemaUrl(servicePkgJsonFileData.$schema),
497
510
  fields: [],
498
- plugins,
511
+ plugins
499
512
  }
500
513
  }
501
514
 
502
515
  // implement in the subclass
503
516
  /* c8 ignore next 1 */
504
- async postInstallActions () { }
505
- async _beforePrepare () { }
506
- async _afterPrepare () { }
507
- async _getConfigFileContents () { return {} }
517
+ async postInstallActions () {}
518
+ async _beforePrepare () {}
519
+ async _afterPrepare () {}
520
+ async _getConfigFileContents () {
521
+ return {}
522
+ }
508
523
  }
509
524
 
510
525
  function serializeEnvVars (envVars) {
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
  const { createDirectory } = require('@platformatic/utils')
3
- const { join } = require('node:path')
3
+ const { dirname, join, isAbsolute } = require('node:path')
4
4
  const { writeFile, readFile } = require('node:fs/promises')
5
5
 
6
6
  /* c8 ignore start */
@@ -9,7 +9,7 @@ const fakeLogger = {
9
9
  warn: () => {},
10
10
  debug: () => {},
11
11
  trace: () => {},
12
- error: () => {},
12
+ error: () => {}
13
13
  }
14
14
  /* c8 ignore start */
15
15
 
@@ -31,15 +31,16 @@ class FileGenerator {
31
31
  return this.getFileObject(file, path)
32
32
  }
33
33
 
34
- addFile ({ path, file, contents, options = {} }) {
34
+ addFile ({ path, file, contents, options = {}, tags }) {
35
35
  const fileObject = this.getFileObject(file, path)
36
36
  if (path.startsWith('/')) {
37
37
  path = path.substring(1)
38
38
  }
39
39
  if (fileObject) {
40
40
  fileObject.contents = contents
41
+ fileObject.tags = tags ?? []
41
42
  } else {
42
- this.files.push({ path, file, contents, options })
43
+ this.files.push({ path, file, contents, options, tags: tags ?? [] })
43
44
  }
44
45
  }
45
46
 
@@ -64,26 +65,32 @@ class FileGenerator {
64
65
  if (fileToWrite.contents.length === 0 && !fileToWrite.file.match(/^\..+keep$/)) {
65
66
  continue
66
67
  }
67
- const baseDir = join(this.targetDirectory, fileToWrite.path)
68
- if (fileToWrite.path !== '') {
69
- await createDirectory(baseDir)
68
+
69
+ let fullFilePath = join(fileToWrite.path, fileToWrite.file)
70
+
71
+ if (!isAbsolute(fullFilePath)) {
72
+ fullFilePath = join(this.targetDirectory, fullFilePath)
70
73
  }
71
- const fullFilePath = join(baseDir, fileToWrite.file)
74
+
75
+ await createDirectory(dirname(fullFilePath))
72
76
  await writeFile(fullFilePath, fileToWrite.contents, fileToWrite.options)
77
+
73
78
  this.logger.info(`${fullFilePath} written!`)
74
79
  }
75
80
  }
76
81
 
77
82
  getFileObject (name, path = '') {
78
- const output = this.files.find((file) => {
83
+ const output = this.files.find(file => {
79
84
  return file.path === path && file.file === name
80
85
  })
81
- if (!output) { return null }
86
+ if (!output) {
87
+ return null
88
+ }
82
89
  return output
83
90
  }
84
91
 
85
92
  listFiles () {
86
- return this.files.map((fileObject) => {
93
+ return this.files.map(fileObject => {
87
94
  return join(fileObject.path, fileObject.file)
88
95
  })
89
96
  }
@@ -0,0 +1,230 @@
1
+ 'use strict'
2
+
3
+ const { findConfigurationFile } = require('@platformatic/config')
4
+ const { safeRemove } = require('@platformatic/utils')
5
+ const { glob } = require('glob')
6
+ const { BaseGenerator } = require('./base-generator')
7
+ const { spawnSync } = require('node:child_process')
8
+ const { stat, readFile } = require('node:fs/promises')
9
+ const { join, dirname } = require('node:path')
10
+
11
+ class ImportGenerator extends BaseGenerator {
12
+ constructor (options = {}) {
13
+ const { serviceName, module, version, parent: runtime, ...opts } = options
14
+ super({ ...opts, module })
15
+
16
+ this.runtime = runtime
17
+ this.setConfig({
18
+ serviceName,
19
+ servicePathEnvName: `PLT_SERVICE_${serviceName.toUpperCase().replaceAll(/[^A-Z0-9_]/g, '_')}_PATH`,
20
+ module,
21
+ version
22
+ })
23
+ }
24
+
25
+ async prepareQuestions () {
26
+ await super.prepareQuestions()
27
+
28
+ this.questions.push({
29
+ type: 'input',
30
+ name: 'applicationPath',
31
+ message: 'Where is your application located?',
32
+ async validate (value) {
33
+ if (value.length === 0) {
34
+ return 'Please enter a path'
35
+ }
36
+
37
+ try {
38
+ const pathStat = await stat(value)
39
+
40
+ if (!pathStat?.isDirectory()) {
41
+ return 'Please enter a valid path'
42
+ }
43
+ } catch {
44
+ return 'Please enter a valid path'
45
+ }
46
+
47
+ return true
48
+ }
49
+ })
50
+
51
+ this.questions.push({
52
+ type: 'list',
53
+ name: 'operation',
54
+ message: 'Do you want to import or copy your application?',
55
+ default: 'import',
56
+ choices: [
57
+ { name: 'import', value: 'import' },
58
+ { name: 'copy', value: 'copy' }
59
+ ]
60
+ })
61
+ }
62
+
63
+ // We can't use prepare as it is not invoked from the runtime when dealing with existing applications
64
+ /* c8 ignore next 3 - Invoked from base-generator */
65
+ async prepare () {
66
+ return { targetDirectory: this.targetDirectory, env: this.config.env }
67
+ }
68
+
69
+ async _beforeWriteFiles (runtime) {
70
+ const { module: pkg, version, applicationPath: path } = this.config
71
+
72
+ const packageJsonPath = join(path, 'package.json')
73
+
74
+ if (this.config.operation === 'copy') {
75
+ await this.#copy(path)
76
+ await this.#generateConfigFile(path, '')
77
+ await this.#updatePackageJson(packageJsonPath, 'package.json', pkg, version)
78
+ } else {
79
+ await this.#detectGitUrl(path)
80
+ await this.#generateConfigFile(path, path)
81
+
82
+ await this.#updatePackageJson(packageJsonPath, packageJsonPath, pkg, version)
83
+ await this.#updateRuntime(runtime)
84
+ }
85
+ }
86
+
87
+ async _afterWriteFiles (runtime) {
88
+ // No need for an empty folder in the services folder
89
+ if (this.config.operation === 'import') {
90
+ await safeRemove(join(runtime.servicesBasePath, this.config.serviceName))
91
+ }
92
+ }
93
+
94
+ async #detectGitUrl (path) {
95
+ let url
96
+
97
+ // First of all, determine if there is a git repository in the application path
98
+ // Detect if there is a git folder and eventually get the remote
99
+ for (const candidate of ['origin', 'upstream']) {
100
+ try {
101
+ const result = spawnSync('git', ['remote', 'get-url', candidate], { cwd: path })
102
+
103
+ /* c8 ignore next 3 - Hard to test */
104
+ if (result.error || result.status !== 0) {
105
+ continue
106
+ }
107
+
108
+ url = result.stdout.toString().trim()
109
+ break
110
+ /* c8 ignore next 3 - Hard to test */
111
+ } catch (e) {
112
+ // No-op
113
+ }
114
+ }
115
+
116
+ this.setConfig({ gitUrl: url })
117
+ return url
118
+ }
119
+
120
+ async #generateConfigFile (originalPath, updatedPath) {
121
+ // Determine if there is a watt.json file in the application path - If it's missing, insert one
122
+ // For import it means we don't update the file, for copy it means it was already copied in #copy.
123
+ const existingConfig = await findConfigurationFile(originalPath)
124
+
125
+ if (existingConfig) {
126
+ return
127
+ }
128
+
129
+ const { module: pkg, version } = this.config
130
+
131
+ if (pkg.startsWith('@platformatic/')) {
132
+ this.addFile({
133
+ path: '',
134
+ file: join(updatedPath, this.runtimeConfig),
135
+ contents: JSON.stringify({ $schema: `https://schemas.platformatic.dev/${pkg}/${version}.json` }, null, 2)
136
+ })
137
+ } else {
138
+ this.addFile({
139
+ path: '',
140
+ file: join(updatedPath, this.runtimeConfig),
141
+ contents: JSON.stringify({ module: pkg }, null, 2)
142
+ })
143
+ }
144
+ }
145
+
146
+ async #updatePackageJson (originalPath, updatedPath, pkg, version) {
147
+ // Add the module to the package.json dependencies
148
+ let packageJson = {}
149
+
150
+ try {
151
+ packageJson = JSON.parse(await readFile(originalPath, 'utf-8'))
152
+ } catch (e) {
153
+ // No-op, we will create a new package.json
154
+ }
155
+
156
+ packageJson.dependencies ??= {}
157
+ packageJson.dependencies[pkg] = `^${version}`
158
+ if (packageJson.devDependencies?.[pkg]) {
159
+ packageJson.devDependencies[pkg] = undefined
160
+ }
161
+
162
+ this.addFile({ path: '', file: updatedPath, contents: JSON.stringify(packageJson, null, 2) })
163
+ }
164
+
165
+ async #updateRuntime (runtime) {
166
+ const configObject = runtime.getRuntimeConfigFileObject()
167
+ /* c8 ignore next - else */
168
+ const config = JSON.parse(configObject?.contents ?? '{}')
169
+ const envObject = runtime.getRuntimeEnvFileObject()
170
+ /* c8 ignore next - else */
171
+ let env = envObject?.contents ?? ''
172
+
173
+ // Find which key is being used for the manual services
174
+ let key
175
+ for (const candidate of new Set([runtime.servicesFolder, 'web', 'services'])) {
176
+ if (Array.isArray(config[candidate])) {
177
+ key = candidate
178
+ break
179
+ }
180
+ }
181
+
182
+ /* c8 ignore next - else */
183
+ key ??= runtime.servicesFolder ?? 'services'
184
+ const services = config[key] ?? []
185
+
186
+ if (!services.some(service => service.id === this.config.serviceName)) {
187
+ services.push({
188
+ id: this.config.serviceName,
189
+ path: `{${this.config.servicePathEnvName}}`,
190
+ url: this.config.gitUrl
191
+ })
192
+ }
193
+
194
+ config[key] = services
195
+
196
+ if (env.length > 0) {
197
+ env += '\n'
198
+ }
199
+ env += `${this.config.servicePathEnvName}=${this.config.applicationPath}`
200
+
201
+ runtime.updateRuntimeConfig(config)
202
+ runtime.updateRuntimeEnv(env)
203
+ }
204
+
205
+ async #copy (root) {
206
+ const files = await glob('**/*', { cwd: root, dot: true, ignore: ['node_modules/**'], withFileTypes: true })
207
+
208
+ for (const file of files) {
209
+ if (file.isDirectory()) {
210
+ continue
211
+ }
212
+
213
+ /* c8 ignore next 6 */
214
+ let path = dirname(file.relative())
215
+ if (path === '.') {
216
+ path = ''
217
+ } else if (path.startsWith('./')) {
218
+ path = path.substring(2)
219
+ }
220
+
221
+ this.addFile({
222
+ path,
223
+ file: file.name,
224
+ contents: await readFile(file.fullpath())
225
+ })
226
+ }
227
+ }
228
+ }
229
+
230
+ module.exports = { ImportGenerator }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/generators",
3
- "version": "2.67.0-alpha.2",
3
+ "version": "2.67.1",
4
4
  "description": "Main classes and utils for generators.",
5
5
  "main": "index.js",
6
6
  "keywords": [],
@@ -9,10 +9,13 @@
9
9
  "dependencies": {
10
10
  "@fastify/error": "^4.0.0",
11
11
  "change-case-all": "^2.1.0",
12
+ "execa": "^9.6.0",
12
13
  "fastify": "^5.0.0",
14
+ "glob": "^11.0.2",
13
15
  "pino": "^9.0.0",
14
16
  "undici": "^7.0.0",
15
- "@platformatic/utils": "2.67.0-alpha.2"
17
+ "@platformatic/utils": "2.67.1",
18
+ "@platformatic/config": "2.67.1"
16
19
  },
17
20
  "devDependencies": {
18
21
  "@types/inquirer": "^9.0.7",