create-platformatic 0.45.1 → 0.46.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-platformatic",
3
- "version": "0.45.1",
3
+ "version": "0.46.1",
4
4
  "description": "Create platformatic-db interactive tool",
5
5
  "repository": {
6
6
  "type": "git",
@@ -34,7 +34,7 @@
34
34
  "semver": "^7.5.1",
35
35
  "undici": "^5.22.1",
36
36
  "which": "^3.0.1",
37
- "@platformatic/config": "0.45.1"
37
+ "@platformatic/config": "0.46.1"
38
38
  },
39
39
  "devDependencies": {
40
40
  "ajv": "^8.12.0",
@@ -47,8 +47,8 @@
47
47
  "tap": "^16.3.6",
48
48
  "typescript": "~5.2.0",
49
49
  "yaml": "^2.3.1",
50
- "@platformatic/db": "0.45.1",
51
- "@platformatic/service": "0.45.1"
50
+ "@platformatic/db": "0.46.1",
51
+ "@platformatic/service": "0.46.1"
52
52
  },
53
53
  "scripts": {
54
54
  "test": "standard | snazzy && cross-env NODE_OPTIONS=\"--loader=esmock --no-warnings\" c8 tap --no-coverage test/*test.mjs test/*/*test.mjs",
@@ -93,8 +93,8 @@ const createPlatformaticComposer = async (_args, opts) => {
93
93
  }
94
94
 
95
95
  if (!opts.skipGitHubActions) {
96
- await askDynamicWorkspaceCreateGHAction(logger, env, 'composer', false, projectDir)
97
96
  await askStaticWorkspaceGHAction(logger, env, 'composer', false, projectDir)
97
+ await askDynamicWorkspaceCreateGHAction(logger, env, 'composer', false, projectDir)
98
98
  }
99
99
  }
100
100
 
@@ -0,0 +1,232 @@
1
+ import { join } from 'path'
2
+ import { writeFile, mkdir } from 'fs/promises'
3
+ import { isFileAccessible } from './utils.mjs'
4
+
5
+ const JS_PLUGIN_WITH_TYPES_SUPPORT = `\
6
+ /// <reference path="../global.d.ts" />
7
+ 'use strict'
8
+ /** @param {import('fastify').FastifyInstance} fastify */
9
+ module.exports = async function (fastify, opts) {
10
+ fastify.decorate('example', 'foobar')
11
+ }
12
+ `
13
+
14
+ const TS_PLUGIN_WITH_TYPES_SUPPORT = `\
15
+ /// <reference path="../global.d.ts" />
16
+ import { FastifyInstance, FastifyPluginOptions } from 'fastify'
17
+
18
+ export default async function (fastify: FastifyInstance, opts: FastifyPluginOptions) {
19
+ fastify.decorate('example', 'foobar')
20
+ }
21
+ `
22
+
23
+ const JS_ROUTES_WITH_TYPES_SUPPORT = `\
24
+ /// <reference path="../global.d.ts" />
25
+ 'use strict'
26
+ /** @param {import('fastify').FastifyInstance} fastify */
27
+ module.exports = async function (fastify, opts) {
28
+ fastify.get('/example', async (request, reply) => {
29
+ return { hello: fastify.example }
30
+ })
31
+ }
32
+ `
33
+
34
+ const TS_ROUTES_WITH_TYPES_SUPPORT = `\
35
+ /// <reference path="../global.d.ts" />
36
+ import { FastifyInstance, FastifyPluginOptions } from 'fastify'
37
+
38
+ declare module 'fastify' {
39
+ interface FastifyInstance {
40
+ example: string
41
+ }
42
+ }
43
+
44
+ export default async function (fastify: FastifyInstance, opts: FastifyPluginOptions) {
45
+ fastify.get('/example', async (request, reply) => {
46
+ return { hello: fastify.example }
47
+ })
48
+ }
49
+ `
50
+
51
+ function testHelperJS (mod, customization = { pre: '', post: '', config: '' }) {
52
+ return `\
53
+ 'use strict'
54
+
55
+ const { join } = require('node:path')
56
+ const { readFile } = require('node:fs/promises')
57
+ const { buildServer } = require('@platformatic/${mod}')
58
+ ${customization.requires || ''}
59
+
60
+ async function getServer (t) {
61
+ ${customization.pre || ''}
62
+ const config = JSON.parse(await readFile(join(__dirname, '..', 'platformatic.${mod}.json'), 'utf8'))
63
+ // Add your config customizations here. For example you want to set
64
+ // all things that are set in the config file to read from an env variable
65
+ config.server.logger.level = 'warn'
66
+ config.watch = false
67
+ ${customization.config || ''}
68
+ // Add your config customizations here
69
+ const server = await buildServer(config)
70
+ t.after(() => server.close())
71
+ ${customization.post || ''}
72
+ return server
73
+ }
74
+
75
+ module.exports.getServer = getServer
76
+ `
77
+ }
78
+
79
+ const TEST_ROUTES_JS = `\
80
+ 'use strict'
81
+
82
+ const test = require('node:test')
83
+ const assert = require('node:assert')
84
+ const { getServer } = require('../helper')
85
+
86
+ test('example', async (t) => {
87
+ const server = await getServer(t)
88
+ const res = await server.inject({
89
+ method: 'GET',
90
+ url: '/example'
91
+ })
92
+
93
+ assert.strictEqual(res.statusCode, 200)
94
+ assert.deepStrictEqual(res.json(), {
95
+ hello: 'foobar'
96
+ })
97
+ })
98
+ `
99
+
100
+ const TEST_PLUGIN_JS = `\
101
+ 'use strict'
102
+
103
+ const test = require('node:test')
104
+ const assert = require('node:assert')
105
+ const { getServer } = require('../helper')
106
+
107
+ test('example decorator', async (t) => {
108
+ const server = await getServer(t)
109
+
110
+ assert.strictEqual(server.example, 'foobar')
111
+ })
112
+ `
113
+
114
+ function testHelperTS (mod, customizations = { pre: '', post: '', config: '', requires: '' }) {
115
+ return `\
116
+ import { join } from 'node:path'
117
+ import { readFile } from 'node:fs/promises'
118
+ import { buildServer } from '@platformatic/${mod}'
119
+ ${customizations.requires}
120
+
121
+ export async function getServer (t) {
122
+ ${customizations.pre}
123
+ // We go up two folder because this files executes in the dist folder
124
+ const config = JSON.parse(await readFile(join(__dirname, '..', '..', 'platformatic.${mod}.json'), 'utf8'))
125
+ // Add your config customizations here. For example you want to set
126
+ // all things that are set in the config file to read from an env variable
127
+ config.server.logger.level = 'warn'
128
+ config.watch = false
129
+ ${customizations.config}
130
+ // Add your config customizations here
131
+ const server = await buildServer(config)
132
+ t.after(() => server.close())
133
+ ${customizations.post}
134
+ return server
135
+ }
136
+ `
137
+ }
138
+
139
+ const TEST_ROUTES_TS = `\
140
+ import test from 'node:test'
141
+ import assert from 'node:assert'
142
+ import { getServer } from '../helper'
143
+
144
+ test('root', async (t) => {
145
+ const server = await getServer(t)
146
+ const res = await server.inject({
147
+ method: 'GET',
148
+ url: '/example'
149
+ })
150
+
151
+ assert.strictEqual(res.statusCode, 200)
152
+ assert.deepStrictEqual(res.json(), {
153
+ hello: 'foobar'
154
+ })
155
+ })
156
+ `
157
+
158
+ const TEST_PLUGIN_TS = `\
159
+ import test from 'node:test'
160
+ import assert from 'node:assert'
161
+ import { getServer } from '../helper'
162
+
163
+ test('example decorator', async (t) => {
164
+ const server = await getServer(t)
165
+
166
+ assert.strictEqual(server.example, 'foobar')
167
+ })
168
+ `
169
+
170
+ export async function generatePluginWithTypesSupport (logger, currentDir, isTypescript) {
171
+ const accessible = await isFileAccessible('plugins', currentDir)
172
+ if (accessible) {
173
+ logger.info('Plugins folder "plugins" found, skipping creation of plugins folder.')
174
+ return
175
+ }
176
+ await mkdir(join(currentDir, 'plugins'))
177
+ const pluginTemplate = isTypescript
178
+ ? TS_PLUGIN_WITH_TYPES_SUPPORT
179
+ : JS_PLUGIN_WITH_TYPES_SUPPORT
180
+ const pluginName = isTypescript
181
+ ? 'example.ts'
182
+ : 'example.js'
183
+ await writeFile(join(currentDir, 'plugins', pluginName), pluginTemplate)
184
+ logger.info('Plugins folder "plugins" successfully created.')
185
+ }
186
+
187
+ export async function generateRouteWithTypesSupport (logger, currentDir, isTypescript) {
188
+ const accessible = await isFileAccessible('routes', currentDir)
189
+ if (accessible) {
190
+ logger.info('Routes folder "routes" found, skipping creation of routes folder.')
191
+ return
192
+ }
193
+ await mkdir(join(currentDir, 'routes'))
194
+ const routesTemplate = isTypescript
195
+ ? TS_ROUTES_WITH_TYPES_SUPPORT
196
+ : JS_ROUTES_WITH_TYPES_SUPPORT
197
+ const routesName = isTypescript
198
+ ? 'root.ts'
199
+ : 'root.js'
200
+ await writeFile(join(currentDir, 'routes', routesName), routesTemplate)
201
+ logger.info('Routes folder "routes" successfully created.')
202
+ }
203
+
204
+ export async function generateTests (logger, currentDir, isTypescript, mod, customizations) {
205
+ const accessible = await isFileAccessible('tests', currentDir)
206
+ if (accessible) {
207
+ logger.info('Test folder found, skipping creation of tests.')
208
+ return
209
+ }
210
+
211
+ await mkdir(join(currentDir, 'test'))
212
+ await mkdir(join(currentDir, 'test', 'plugins'))
213
+ await mkdir(join(currentDir, 'test', 'routes'))
214
+
215
+ if (isTypescript) {
216
+ await writeFile(join(currentDir, 'test', 'helper.ts'), testHelperTS(mod, customizations))
217
+ await writeFile(join(currentDir, 'test', 'plugins', 'example.test.ts'), TEST_PLUGIN_TS)
218
+ await writeFile(join(currentDir, 'test', 'routes', 'root.test.ts'), TEST_ROUTES_TS)
219
+ } else {
220
+ await writeFile(join(currentDir, 'test', 'helper.js'), testHelperJS(mod, customizations))
221
+ await writeFile(join(currentDir, 'test', 'plugins', 'example.test.js'), TEST_PLUGIN_JS)
222
+ await writeFile(join(currentDir, 'test', 'routes', 'root.test.js'), TEST_ROUTES_JS)
223
+ }
224
+
225
+ logger.info('Test folder "tests" successfully created.')
226
+ }
227
+
228
+ export async function generatePlugins (logger, currentDir, isTypescript, mod, helperCustomization) {
229
+ await generatePluginWithTypesSupport(logger, currentDir, isTypescript)
230
+ await generateRouteWithTypesSupport(logger, currentDir, isTypescript)
231
+ await generateTests(logger, currentDir, isTypescript, mod, helperCustomization)
232
+ }
@@ -166,7 +166,8 @@ const createPlatformaticDB = async (_args, opts) => {
166
166
  const fastifyVersion = await getDependencyVersion('fastify')
167
167
 
168
168
  const scripts = {
169
- migrate: 'platformatic db migrations apply'
169
+ migrate: 'platformatic db migrations apply',
170
+ test: useTypescript ? 'tsc && node --test dist/test/*/*.test.js' : 'node --test test/*/*.test.js'
170
171
  }
171
172
 
172
173
  const dependencies = {
@@ -245,8 +246,8 @@ const createPlatformaticDB = async (_args, opts) => {
245
246
  }
246
247
 
247
248
  if (!opts.skipGitHubActions) {
248
- await askDynamicWorkspaceCreateGHAction(logger, env, 'db', useTypescript, projectDir)
249
249
  await askStaticWorkspaceGHAction(logger, env, 'db', useTypescript, projectDir)
250
+ await askDynamicWorkspaceCreateGHAction(logger, env, 'db', useTypescript, projectDir)
250
251
  }
251
252
  }
252
253
 
@@ -1,7 +1,8 @@
1
1
  import { writeFile, mkdir, appendFile } from 'fs/promises'
2
- import { join, relative, resolve } from 'path'
2
+ import { join } from 'path'
3
3
  import { findDBConfigFile, isFileAccessible } from '../utils.mjs'
4
4
  import { getTsConfig } from '../get-tsconfig.mjs'
5
+ import { generatePlugins } from '../create-plugins.mjs'
5
6
 
6
7
  const connectionStrings = {
7
8
  postgres: 'postgres://postgres:postgres@127.0.0.1:5432/postgres',
@@ -32,9 +33,222 @@ const moviesMigrationUndo = `
32
33
  DROP TABLE movies;
33
34
  `
34
35
 
35
- const getPluginName = (isTypescript) => isTypescript === true ? 'plugin.ts' : 'plugin.js'
36
36
  const TS_OUT_DIR = 'dist'
37
37
 
38
+ const jsHelperSqlite = {
39
+ requires: `
40
+ const os = require('node:os')
41
+ const path = require('node:path')
42
+ const fs = require('node:fs/promises')
43
+
44
+ let counter = 0
45
+ `,
46
+ pre: `
47
+ const dbPath = join(os.tmpdir(), 'db-' + process.pid + '-' + counter++ + '.sqlite')
48
+ const connectionString = 'sqlite://' + dbPath
49
+ `,
50
+ config: `
51
+ config.migrations.autoApply = true
52
+ config.types.autogenerate = false
53
+ config.db.connectionString = connectionString
54
+ `,
55
+ post: `
56
+ t.after(async () => {
57
+ await fs.unlink(dbPath)
58
+ })
59
+ `
60
+ }
61
+
62
+ function jsHelperPostgres (connectionString) {
63
+ return {
64
+ // TODO(mcollina): replace sql-mapper
65
+ requires: `
66
+ const { createConnectionPool } = require('@platformatic/sql-mapper')
67
+ const connectionString = '${connectionString}'
68
+ let counter = 0
69
+ `,
70
+ pre: `
71
+ const { db, sql } = await createConnectionPool({
72
+ log: {
73
+ debug: () => {},
74
+ info: () => {},
75
+ trace: () => {}
76
+ },
77
+ connectionString,
78
+ poolSize: 1
79
+ })
80
+
81
+ const newDB = \`t-\${process.pid}-\${counter++}\`
82
+ t.diagnostic('Creating database ' + newDB)
83
+
84
+ await db.query(sql\`
85
+ CREATE DATABASE \${sql.ident(newDB)}
86
+ \`)
87
+ `,
88
+ config: `
89
+ config.migrations.autoApply = true
90
+ config.types.autogenerate = false
91
+ config.db.connectionString = connectionString.replace(/\\/[a-zA-Z0-9\\-_]+$/, '/' + newDB)
92
+ config.db.schemalock = false
93
+ `,
94
+ post: `
95
+ t.after(async () => {
96
+ t.diagnostic('Disposing test database ' + newDB)
97
+ await db.query(sql\`
98
+ DROP DATABASE \${sql.ident(newDB)}
99
+ \`)
100
+ await db.dispose()
101
+ })
102
+ `
103
+ }
104
+ }
105
+
106
+ function jsHelperMySQL (connectionString) {
107
+ return {
108
+ // TODO(mcollina): replace sql-mapper
109
+ requires: `
110
+ const { createConnectionPool } = require('@platformatic/sql-mapper')
111
+ const connectionString = '${connectionString}'
112
+ let counter = 0
113
+ `,
114
+ pre: `
115
+ const { db, sql } = await createConnectionPool({
116
+ log: {
117
+ debug: () => {},
118
+ info: () => {},
119
+ trace: () => {}
120
+ },
121
+ connectionString,
122
+ poolSize: 1
123
+ })
124
+
125
+ const newDB = \`t-\${process.pid}-\${counter++}\`
126
+ t.diagnostic('Creating database ' + newDB)
127
+
128
+ await db.query(sql\`
129
+ CREATE DATABASE \${sql.ident(newDB)}
130
+ \`)
131
+ `,
132
+ config: `
133
+ config.migrations.autoApply = true
134
+ config.types.autogenerate = false
135
+ config.db.connectionString = connectionString.replace(/\\/[a-zA-Z0-9\\-_]+$/, '/' + newDB)
136
+ config.db.schemalock = false
137
+ `,
138
+ post: `
139
+ t.after(async () => {
140
+ t.diagnostic('Disposing test database ' + newDB)
141
+ await db.query(sql\`
142
+ DROP DATABASE \${sql.ident(newDB)}
143
+ \`)
144
+ await db.dispose()
145
+ })
146
+ `
147
+ }
148
+ }
149
+
150
+ const moviesTestJS = `\
151
+ 'use strict'
152
+
153
+ const test = require('node:test')
154
+ const assert = require('node:assert')
155
+ const { getServer } = require('../helper')
156
+
157
+ test('movies', async (t) => {
158
+ const server = await getServer(t)
159
+
160
+ {
161
+ const res = await server.inject({
162
+ method: 'GET',
163
+ url: '/movies'
164
+ })
165
+
166
+ assert.strictEqual(res.statusCode, 200)
167
+ assert.deepStrictEqual(res.json(), [])
168
+ }
169
+
170
+ let id
171
+ {
172
+ const res = await server.inject({
173
+ method: 'POST',
174
+ url: '/movies',
175
+ body: {
176
+ title: 'The Matrix'
177
+ }
178
+ })
179
+
180
+ assert.strictEqual(res.statusCode, 200)
181
+ const body = res.json()
182
+ assert.strictEqual(body.title, 'The Matrix')
183
+ assert.strictEqual(body.id !== undefined, true)
184
+ id = body.id
185
+ }
186
+
187
+ {
188
+ const res = await server.inject({
189
+ method: 'GET',
190
+ url: '/movies'
191
+ })
192
+
193
+ assert.strictEqual(res.statusCode, 200)
194
+ assert.deepStrictEqual(res.json(), [{
195
+ id,
196
+ title: 'The Matrix'
197
+ }])
198
+ }
199
+ })
200
+ `
201
+
202
+ const moviesTestTS = `\
203
+ import test from 'node:test'
204
+ import assert from 'node:assert'
205
+ import { getServer } from '../helper'
206
+
207
+ test('movies', async (t) => {
208
+ const server = await getServer(t)
209
+
210
+ {
211
+ const res = await server.inject({
212
+ method: 'GET',
213
+ url: '/movies'
214
+ })
215
+
216
+ assert.strictEqual(res.statusCode, 200)
217
+ assert.deepStrictEqual(res.json(), [])
218
+ }
219
+
220
+ let id : Number
221
+ {
222
+ const res = await server.inject({
223
+ method: 'POST',
224
+ url: '/movies',
225
+ body: {
226
+ title: 'The Matrix'
227
+ }
228
+ })
229
+
230
+ assert.strictEqual(res.statusCode, 200)
231
+ const body = res.json()
232
+ assert.strictEqual(body.title, 'The Matrix')
233
+ assert.strictEqual(body.id !== undefined, true)
234
+ id = body.id as Number
235
+ }
236
+
237
+ {
238
+ const res = await server.inject({
239
+ method: 'GET',
240
+ url: '/movies'
241
+ })
242
+
243
+ assert.strictEqual(res.statusCode, 200)
244
+ assert.deepStrictEqual(res.json(), [{
245
+ id,
246
+ title: 'The Matrix'
247
+ }])
248
+ }
249
+ })
250
+ `
251
+
38
252
  function generateConfig (migrations, plugin, types, typescript, version) {
39
253
  const config = {
40
254
  $schema: `https://platformatic.dev/schemas/v${version}/db`,
@@ -64,7 +278,12 @@ function generateConfig (migrations, plugin, types, typescript, version) {
64
278
 
65
279
  if (plugin === true) {
66
280
  config.plugins = {
67
- paths: [getPluginName(typescript)]
281
+ paths: [{
282
+ path: './plugins',
283
+ encapsulate: false
284
+ }, {
285
+ path: './routes'
286
+ }]
68
287
  }
69
288
  }
70
289
 
@@ -101,38 +320,6 @@ PLT_TYPESCRIPT=true
101
320
  return env
102
321
  }
103
322
 
104
- const JS_PLUGIN_WITH_TYPES_SUPPORT = `\
105
- /// <reference path="./global.d.ts" />
106
- 'use strict'
107
-
108
- /** @param {import('fastify').FastifyInstance} app */
109
- module.exports = async function (app) {}
110
- `
111
-
112
- const TS_PLUGIN_WITH_TYPES_SUPPORT = `\
113
- /// <reference path="./global.d.ts" />
114
- import { FastifyInstance } from 'fastify'
115
-
116
- export default async function (app: FastifyInstance) {}
117
- `
118
-
119
- async function generatePluginWithTypesSupport (logger, currentDir, isTypescript) {
120
- const pluginPath = resolve(currentDir, getPluginName(isTypescript))
121
-
122
- const isPluginExists = await isFileAccessible(pluginPath)
123
- if (isPluginExists) {
124
- logger.info(`Plugin file ${pluginPath} found, skipping creation of plugin file.`)
125
- return
126
- }
127
-
128
- const pluginTemplate = isTypescript
129
- ? TS_PLUGIN_WITH_TYPES_SUPPORT
130
- : JS_PLUGIN_WITH_TYPES_SUPPORT
131
-
132
- await writeFile(pluginPath, pluginTemplate)
133
- logger.info(`Plugin file created at ${relative(currentDir, pluginPath)}`)
134
- }
135
-
136
323
  export function getConnectionString (database) {
137
324
  return connectionStrings[database]
138
325
  }
@@ -201,7 +388,30 @@ export async function createDB ({ hostname, database = 'sqlite', port, migration
201
388
  }
202
389
 
203
390
  if (plugin) {
204
- await generatePluginWithTypesSupport(logger, currentDir, typescript)
391
+ let jsHelper = { pre: '', config: '', post: '' }
392
+ switch (database) {
393
+ case 'sqlite':
394
+ jsHelper = jsHelperSqlite
395
+ break
396
+ case 'mysql':
397
+ jsHelper = jsHelperMySQL(connectionString)
398
+ break
399
+ case 'postgres':
400
+ jsHelper = jsHelperPostgres(connectionString)
401
+ break
402
+ case 'mariadb':
403
+ jsHelper = jsHelperMySQL(connectionString)
404
+ break
405
+ }
406
+ await generatePlugins(logger, currentDir, typescript, 'db', jsHelper)
407
+
408
+ if (createMigrations) {
409
+ if (typescript) {
410
+ await writeFile(join(currentDir, 'test', 'routes', 'movies.test.ts'), moviesTestTS)
411
+ } else {
412
+ await writeFile(join(currentDir, 'test', 'routes', 'movies.test.js'), moviesTestJS)
413
+ }
414
+ }
205
415
  }
206
416
 
207
417
  const output = {
package/src/ghaction.mjs CHANGED
@@ -176,7 +176,7 @@ export const askStaticWorkspaceGHAction = async (logger, env, type, buildTS, pro
176
176
  {
177
177
  type: 'list',
178
178
  name: 'githubAction',
179
- message: 'Do you want to create the github action to deploy this application to Platformatic Cloud static workspace?',
179
+ message: 'Do you want to create the github action to deploy this application to Platformatic Cloud?',
180
180
  default: true,
181
181
  choices: [{ name: 'yes', value: true }, { name: 'no', value: false }]
182
182
  }
@@ -114,8 +114,8 @@ export async function createPlatformaticRuntime (_args) {
114
114
 
115
115
  const env = await createRuntime(logger, projectDir, version, servicesDir, entrypoint)
116
116
 
117
- await askDynamicWorkspaceCreateGHAction(logger, env, 'service', false, projectDir)
118
117
  await askStaticWorkspaceGHAction(logger, env, 'service', false, projectDir)
118
+ await askDynamicWorkspaceCreateGHAction(logger, env, 'service', false, projectDir)
119
119
  }
120
120
 
121
121
  export async function createRuntimeService ({ servicesDir, names, logger }) {
@@ -78,8 +78,11 @@ const createPlatformaticService = async (_args, opts = {}) => {
78
78
  const fastifyVersion = await getDependencyVersion('fastify')
79
79
 
80
80
  if (!opts.skipPackageJson) {
81
- await createPackageJson(version, fastifyVersion, logger, projectDir, useTypescript, {}, {
82
- '@platformatic/service': '^' + version
81
+ const test = useTypescript ? 'tsc && node --test dist/test/*/*.test.js' : 'node --test test/*/*.test.js'
82
+ await createPackageJson(version, fastifyVersion, logger, projectDir, useTypescript, {
83
+ test
84
+ }, {
85
+ '@platformatic/service': `^${version}`
83
86
  })
84
87
  }
85
88
  if (!opts.skipGitignore) {
@@ -103,8 +106,8 @@ const createPlatformaticService = async (_args, opts = {}) => {
103
106
  }
104
107
 
105
108
  if (!opts.skipGitHubActions) {
106
- await askDynamicWorkspaceCreateGHAction(logger, env, 'service', useTypescript, projectDir)
107
109
  await askStaticWorkspaceGHAction(logger, env, 'service', useTypescript, projectDir)
110
+ await askDynamicWorkspaceCreateGHAction(logger, env, 'service', useTypescript, projectDir)
108
111
  }
109
112
  }
110
113
 
@@ -1,8 +1,9 @@
1
- import { writeFile, mkdir, readFile, appendFile } from 'fs/promises'
1
+ import { writeFile, readFile, appendFile } from 'fs/promises'
2
2
  import { join } from 'path'
3
3
  import * as desm from 'desm'
4
4
  import { findServiceConfigFile, isFileAccessible } from '../utils.mjs'
5
5
  import { getTsConfig } from '../get-tsconfig.mjs'
6
+ import { generatePlugins } from '../create-plugins.mjs'
6
7
 
7
8
  const TS_OUT_DIR = 'dist'
8
9
 
@@ -55,76 +56,6 @@ PLT_TYPESCRIPT=true
55
56
  return env
56
57
  }
57
58
 
58
- const JS_PLUGIN_WITH_TYPES_SUPPORT = `\
59
- /// <reference path="../global.d.ts" />
60
- 'use strict'
61
- /** @param {import('fastify').FastifyInstance} fastify */
62
- module.exports = async function (fastify, opts) {
63
- fastify.decorate('example', 'foobar')
64
- }
65
- `
66
-
67
- const TS_PLUGIN_WITH_TYPES_SUPPORT = `\
68
- /// <reference path="../global.d.ts" />
69
- import { FastifyInstance, FastifyPluginOptions } from 'fastify'
70
-
71
- export default async function (fastify: FastifyInstance, opts: FastifyPluginOptions) {
72
- fastify.decorate('example', 'foobar')
73
- }
74
- `
75
-
76
- const JS_ROUTES_WITH_TYPES_SUPPORT = `\
77
- /// <reference path="../global.d.ts" />
78
- 'use strict'
79
- /** @param {import('fastify').FastifyInstance} fastify */
80
- module.exports = async function (fastify, opts) {
81
- fastify.get('/', async (request, reply) => {
82
- return { hello: fastify.example }
83
- })
84
- }
85
- `
86
-
87
- const TS_ROUTES_WITH_TYPES_SUPPORT = `\
88
- /// <reference path="../global.d.ts" />
89
- import { FastifyInstance, FastifyPluginOptions } from 'fastify'
90
-
91
- declare module 'fastify' {
92
- interface FastifyInstance {
93
- example: string
94
- }
95
- }
96
-
97
- export default async function (fastify: FastifyInstance, opts: FastifyPluginOptions) {
98
- fastify.get('/', async (request, reply) => {
99
- return { hello: fastify.example }
100
- })
101
- }
102
- `
103
-
104
- async function generatePluginWithTypesSupport (logger, currentDir, isTypescript) {
105
- await mkdir(join(currentDir, 'plugins'))
106
- const pluginTemplate = isTypescript
107
- ? TS_PLUGIN_WITH_TYPES_SUPPORT
108
- : JS_PLUGIN_WITH_TYPES_SUPPORT
109
- const pluginName = isTypescript
110
- ? 'example.ts'
111
- : 'example.js'
112
- await writeFile(join(currentDir, 'plugins', pluginName), pluginTemplate)
113
- logger.info('Plugins folder "plugins" successfully created.')
114
- }
115
-
116
- async function generateRouteWithTypesSupport (logger, currentDir, isTypescript) {
117
- await mkdir(join(currentDir, 'routes'))
118
- const routesTemplate = isTypescript
119
- ? TS_ROUTES_WITH_TYPES_SUPPORT
120
- : JS_ROUTES_WITH_TYPES_SUPPORT
121
- const routesName = isTypescript
122
- ? 'root.ts'
123
- : 'root.js'
124
- await writeFile(join(currentDir, 'routes', routesName), routesTemplate)
125
- logger.info('Routes folder "routes" successfully created.')
126
- }
127
-
128
59
  async function createService ({ hostname, port, typescript = false }, logger, currentDir = process.cwd(), version) {
129
60
  if (!version) {
130
61
  const pkg = await readFile(desm.join(import.meta.url, '..', '..', 'package.json'))
@@ -167,19 +98,7 @@ async function createService ({ hostname, port, typescript = false }, logger, cu
167
98
  }
168
99
  }
169
100
 
170
- const pluginFolderExists = await isFileAccessible('plugins', currentDir)
171
- if (!pluginFolderExists) {
172
- await generatePluginWithTypesSupport(logger, currentDir, typescript)
173
- } else {
174
- logger.info('Plugins folder "plugins" found, skipping creation of plugins folder.')
175
- }
176
-
177
- const routeFolderExists = await isFileAccessible('routes', currentDir)
178
- if (!routeFolderExists) {
179
- await generateRouteWithTypesSupport(logger, currentDir, typescript)
180
- } else {
181
- logger.info('Routes folder "routes" found, skipping creation of routes folder.')
182
- }
101
+ await generatePlugins(logger, currentDir, typescript, 'service')
183
102
 
184
103
  const output = {
185
104
  PLT_SERVER_LOGGER_LEVEL: 'info',
@@ -88,7 +88,8 @@ test('creates project with no typescript', async ({ equal }) => {
88
88
  const migrationFileUndo = readFileSync(pathToMigrationFileUndo, 'utf8')
89
89
  equal(migrationFileUndo, moviesMigrationUndo)
90
90
 
91
- equal(await isFileAccessible(join(tmpDir, 'plugin.js')), true)
91
+ equal(await isFileAccessible(join(tmpDir, 'routes', 'root.js')), true)
92
+ equal(await isFileAccessible(join(tmpDir, 'plugins', 'example.js')), true)
92
93
  })
93
94
 
94
95
  test('creates project with no typescript and no plugin', async ({ equal }) => {
@@ -202,9 +203,15 @@ test('creates project with typescript', async ({ equal, same }) => {
202
203
  const migrationFileUndo = readFileSync(pathToMigrationFileUndo, 'utf8')
203
204
  equal(migrationFileUndo, moviesMigrationUndo)
204
205
 
205
- same(plugins.paths, ['plugin.ts'])
206
+ same(plugins.paths, [{
207
+ path: './plugins',
208
+ encapsulate: false
209
+ }, {
210
+ path: './routes'
211
+ }])
206
212
  equal(plugins.typescript, '{PLT_TYPESCRIPT}')
207
- equal(await isFileAccessible(join(tmpDir, 'plugin.ts')), true)
213
+ equal(await isFileAccessible(join(tmpDir, 'plugins', 'example.ts')), true)
214
+ equal(await isFileAccessible(join(tmpDir, 'routes', 'root.ts')), true)
208
215
  equal(await isFileAccessible(join(tmpDir, 'tsconfig.json')), true)
209
216
  })
210
217
 
@@ -256,7 +263,8 @@ test('creates project with tsconfig already present', async ({ ok }) => {
256
263
  })
257
264
 
258
265
  test('creates project with plugin already present', async ({ ok }) => {
259
- const pathToPlugin = join(tmpDir, 'plugin.js')
266
+ const pathToPlugin = join(tmpDir, 'routes', 'root.js')
267
+ mkdirSync(join(tmpDir, 'routes'))
260
268
  writeFileSync(pathToPlugin, 'test')
261
269
  const params = {
262
270
  hostname: 'myhost',
@@ -265,7 +273,7 @@ test('creates project with plugin already present', async ({ ok }) => {
265
273
  types: true
266
274
  }
267
275
  await createDB(params, fakeLogger, tmpDir)
268
- ok(log.includes(`Plugin file ${pathToPlugin} found, skipping creation of plugin file.`))
276
+ ok(log.includes('Routes folder "routes" found, skipping creation of routes folder.'))
269
277
  })
270
278
 
271
279
  test('creates project with no default migrations', async ({ notOk }) => {
@@ -68,6 +68,10 @@ test('creates service with typescript', async ({ equal, same, ok }) => {
68
68
  ok(await isFileAccessible(join(tmpDir, 'tsconfig.json')))
69
69
  ok(await isFileAccessible(join(tmpDir, 'plugins', 'example.ts')))
70
70
  ok(await isFileAccessible(join(tmpDir, 'routes', 'root.ts')))
71
+
72
+ ok(await isFileAccessible(join(tmpDir, 'test', 'plugins', 'example.test.ts')))
73
+ ok(await isFileAccessible(join(tmpDir, 'test', 'routes', 'root.test.ts')))
74
+ ok(await isFileAccessible(join(tmpDir, 'test', 'helper.ts')))
71
75
  })
72
76
 
73
77
  test('creates service with javascript', async ({ equal, same, ok }) => {
@@ -101,6 +105,10 @@ test('creates service with javascript', async ({ equal, same, ok }) => {
101
105
  same(plugins, { paths: [{ path: './plugins', encapsulate: false }, './routes'] })
102
106
  ok(await isFileAccessible(join(tmpDir, 'plugins', 'example.js')))
103
107
  ok(await isFileAccessible(join(tmpDir, 'routes', 'root.js')))
108
+
109
+ ok(await isFileAccessible(join(tmpDir, 'test', 'plugins', 'example.test.js')))
110
+ ok(await isFileAccessible(join(tmpDir, 'test', 'routes', 'root.test.js')))
111
+ ok(await isFileAccessible(join(tmpDir, 'test', 'helper.js')))
104
112
  })
105
113
 
106
114
  test('creates project with configuration already present', async ({ ok }) => {