create-platformatic 0.27.0 → 0.29.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.
- package/create-platformatic.mjs +1 -0
- package/package.json +5 -5
- package/src/composer/create-composer-cli.mjs +1 -1
- package/src/create-gitignore.mjs +5 -1
- package/src/create-package-json.mjs +16 -5
- package/src/db/README.md +1 -1
- package/src/db/create-db-cli.mjs +78 -7
- package/src/db/create-db.mjs +40 -35
- package/src/get-tsconfig.mjs +20 -0
- package/src/index.mjs +10 -1
- package/src/runtime/create-runtime-cli.mjs +24 -10
- package/src/service/create-service-cli.mjs +1 -1
- package/src/service/create-service.mjs +20 -27
- package/src/utils.mjs +13 -2
- package/test/create-package-json.test.mjs +7 -7
- package/test/db/create-db.test.mjs +2 -1
- package/test/exports.test.mjs +8 -0
- package/test/service/create-service.test.mjs +3 -3
package/create-platformatic.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-platformatic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"description": "Create platformatic-db interactive tool",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"semver": "^7.5.1",
|
|
34
34
|
"undici": "^5.22.1",
|
|
35
35
|
"which": "^3.0.1",
|
|
36
|
-
"@platformatic/config": "0.
|
|
36
|
+
"@platformatic/config": "0.29.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"ajv": "^8.12.0",
|
|
@@ -45,11 +45,11 @@
|
|
|
45
45
|
"standard": "^17.1.0",
|
|
46
46
|
"tap": "^16.3.6",
|
|
47
47
|
"yaml": "^2.3.1",
|
|
48
|
-
"@platformatic/db": "0.
|
|
49
|
-
"@platformatic/service": "0.
|
|
48
|
+
"@platformatic/db": "0.29.0",
|
|
49
|
+
"@platformatic/service": "0.29.0"
|
|
50
50
|
},
|
|
51
51
|
"scripts": {
|
|
52
|
-
"test": "standard | snazzy && cross-env NODE_OPTIONS=\"--loader=esmock --no-warnings\" c8
|
|
52
|
+
"test": "standard | snazzy && cross-env NODE_OPTIONS=\"--loader=esmock --no-warnings\" c8 tap --no-coverage test/*test.mjs test/*/*test.mjs",
|
|
53
53
|
"lint": "standard | snazzy"
|
|
54
54
|
}
|
|
55
55
|
}
|
|
@@ -79,7 +79,7 @@ const createPlatformaticComposer = async (_args, opts) => {
|
|
|
79
79
|
// Create the package.json, notes that we don't have the option for TS (yet) so we don't generate
|
|
80
80
|
// the package.json with the TS build
|
|
81
81
|
if (!opts.skipPackageJson) {
|
|
82
|
-
await createPackageJson(
|
|
82
|
+
await createPackageJson(version, fastifyVersion, logger, projectDir, false)
|
|
83
83
|
}
|
|
84
84
|
if (!opts.skipGitignore) {
|
|
85
85
|
await createGitignore(logger, projectDir)
|
package/src/create-gitignore.mjs
CHANGED
|
@@ -31,7 +31,11 @@ tags
|
|
|
31
31
|
# clinicjs
|
|
32
32
|
.clinic/
|
|
33
33
|
`
|
|
34
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Creates a standard Platformatic app .gitignore file
|
|
36
|
+
* @param {import('pino').BaseLogger} logger Logger Interface
|
|
37
|
+
* @param {string} dir Target directory
|
|
38
|
+
*/
|
|
35
39
|
export const createGitignore = async (logger, dir = '.') => {
|
|
36
40
|
const gitignoreFileName = join(dir, '.gitignore')
|
|
37
41
|
const isGitignoreExists = await isFileAccessible(gitignoreFileName)
|
|
@@ -7,10 +7,10 @@ const packageJsonTemplate = (addTSBuild = false) => (`\
|
|
|
7
7
|
{
|
|
8
8
|
"scripts": {
|
|
9
9
|
${addTSBuild
|
|
10
|
-
? `"start": "npm run clean && platformatic
|
|
10
|
+
? `"start": "npm run clean && platformatic start",
|
|
11
11
|
"clean": "rm -fr ./dist",
|
|
12
12
|
"build": "npx tsc"`
|
|
13
|
-
: '"start": "platformatic
|
|
13
|
+
: '"start": "platformatic start"'}
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"fastify": "^{fastifyVersion}"
|
|
@@ -23,12 +23,23 @@ const packageJsonTemplate = (addTSBuild = false) => (`\
|
|
|
23
23
|
}
|
|
24
24
|
}`)
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Creates a Platformatic app package.json file
|
|
28
|
+
* @param {string} platVersion Platformatic Version
|
|
29
|
+
* @param {string} fastifyVersion Fastify Version
|
|
30
|
+
* @param {import('pino').BaseLogger} logger Logger Interface
|
|
31
|
+
* @param {string} dir Target directory where to create the file
|
|
32
|
+
* @param {boolean} addTSBuild Whether to add TS Build or not
|
|
33
|
+
* @param {object} scripts Package.json scripts list
|
|
34
|
+
*/
|
|
35
|
+
export const createPackageJson = async (platVersion, fastifyVersion, logger, dir, addTSBuild = false, scripts = {}) => {
|
|
27
36
|
const packageJsonFileName = join(dir, 'package.json')
|
|
28
37
|
const isPackageJsonExists = await isFileAccessible(packageJsonFileName)
|
|
29
38
|
if (!isPackageJsonExists) {
|
|
30
|
-
const packageJson = pupa(packageJsonTemplate(addTSBuild), { platVersion, fastifyVersion
|
|
31
|
-
|
|
39
|
+
const packageJson = pupa(packageJsonTemplate(addTSBuild), { platVersion, fastifyVersion })
|
|
40
|
+
const parsed = JSON.parse(packageJson)
|
|
41
|
+
Object.assign(parsed.scripts, scripts)
|
|
42
|
+
await writeFile(packageJsonFileName, JSON.stringify(parsed, null, 2))
|
|
32
43
|
logger.debug(`${packageJsonFileName} successfully created.`)
|
|
33
44
|
} else {
|
|
34
45
|
logger.debug(`${packageJsonFileName} found, skipping creation of package.json file.`)
|
package/src/db/README.md
CHANGED
package/src/db/create-db-cli.mjs
CHANGED
|
@@ -11,11 +11,25 @@ import pino from 'pino'
|
|
|
11
11
|
import pretty from 'pino-pretty'
|
|
12
12
|
import { execa } from 'execa'
|
|
13
13
|
import ora from 'ora'
|
|
14
|
-
import createDB from './create-db.mjs'
|
|
14
|
+
import { getConnectionString, createDB } from './create-db.mjs'
|
|
15
15
|
import askDir from '../ask-dir.mjs'
|
|
16
16
|
import { askDynamicWorkspaceCreateGHAction, askStaticWorkspaceGHAction } from '../ghaction.mjs'
|
|
17
17
|
import { getRunPackageManagerInstall, getUseTypescript, getPort, getOverwriteReadme } from '../cli-options.mjs'
|
|
18
18
|
|
|
19
|
+
const databases = [{
|
|
20
|
+
value: 'sqlite',
|
|
21
|
+
name: 'SQLite'
|
|
22
|
+
}, {
|
|
23
|
+
value: 'postgres',
|
|
24
|
+
name: 'PostgreSQL'
|
|
25
|
+
}, {
|
|
26
|
+
value: 'mysql',
|
|
27
|
+
name: 'MySQL'
|
|
28
|
+
}, {
|
|
29
|
+
value: 'mariadb',
|
|
30
|
+
name: 'MariaDB'
|
|
31
|
+
}]
|
|
32
|
+
|
|
19
33
|
export const createReadme = async (logger, dir = '.') => {
|
|
20
34
|
const readmeFileName = join(dir, 'README.md')
|
|
21
35
|
let isReadmeExists = await isFileAccessible(readmeFileName)
|
|
@@ -70,6 +84,48 @@ const createPlatformaticDB = async (_args, opts) => {
|
|
|
70
84
|
const pkgManager = getPkgManager()
|
|
71
85
|
const projectDir = opts.dir || await askDir(logger, '.')
|
|
72
86
|
|
|
87
|
+
const { database } = await inquirer.prompt({
|
|
88
|
+
type: 'list',
|
|
89
|
+
name: 'database',
|
|
90
|
+
message: 'What database do you want to use?',
|
|
91
|
+
default: args.database,
|
|
92
|
+
choices: databases
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
let connectionString = getConnectionString(database)
|
|
96
|
+
|
|
97
|
+
while (true) {
|
|
98
|
+
const pickConnectionString = await inquirer.prompt({
|
|
99
|
+
type: 'expand',
|
|
100
|
+
name: 'edit',
|
|
101
|
+
message: `Do you want to use the connection string "${connectionString}"?`,
|
|
102
|
+
choices: [
|
|
103
|
+
{
|
|
104
|
+
key: 'y',
|
|
105
|
+
name: 'Confirm',
|
|
106
|
+
value: false
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
key: 'e',
|
|
110
|
+
name: 'Edit',
|
|
111
|
+
value: true
|
|
112
|
+
}
|
|
113
|
+
]
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
if (pickConnectionString.edit) {
|
|
117
|
+
const answers = await inquirer.prompt({
|
|
118
|
+
type: 'editor',
|
|
119
|
+
name: 'connectionString',
|
|
120
|
+
message: 'Edit the connection string',
|
|
121
|
+
default: connectionString
|
|
122
|
+
})
|
|
123
|
+
connectionString = answers.connectionString.trim()
|
|
124
|
+
} else {
|
|
125
|
+
break
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
73
129
|
const wizardOptions = await inquirer.prompt([{
|
|
74
130
|
type: 'list',
|
|
75
131
|
name: 'defaultMigrations',
|
|
@@ -97,7 +153,8 @@ const createPlatformaticDB = async (_args, opts) => {
|
|
|
97
153
|
const params = {
|
|
98
154
|
hostname: args.hostname,
|
|
99
155
|
port: wizardOptions.port,
|
|
100
|
-
database
|
|
156
|
+
database,
|
|
157
|
+
connectionString,
|
|
101
158
|
migrations: wizardOptions.defaultMigrations ? args.migrations : '',
|
|
102
159
|
plugin: generatePlugin,
|
|
103
160
|
types: useTypes,
|
|
@@ -108,8 +165,12 @@ const createPlatformaticDB = async (_args, opts) => {
|
|
|
108
165
|
|
|
109
166
|
const fastifyVersion = await getDependencyVersion('fastify')
|
|
110
167
|
|
|
168
|
+
const scripts = {
|
|
169
|
+
migrate: 'platformatic db migrations apply'
|
|
170
|
+
}
|
|
171
|
+
|
|
111
172
|
// Create the package.json, .gitignore, readme
|
|
112
|
-
await createPackageJson(
|
|
173
|
+
await createPackageJson(version, fastifyVersion, logger, projectDir, useTypescript, scripts)
|
|
113
174
|
await createGitignore(logger, projectDir)
|
|
114
175
|
await createReadme(logger, projectDir)
|
|
115
176
|
|
|
@@ -158,8 +219,13 @@ const createPlatformaticDB = async (_args, opts) => {
|
|
|
158
219
|
if (applyMigrations) {
|
|
159
220
|
const spinner = ora('Applying migrations...').start()
|
|
160
221
|
// We need to apply migrations using the platformatic installed in the project
|
|
161
|
-
|
|
162
|
-
|
|
222
|
+
try {
|
|
223
|
+
await execa(pkgManager, ['exec', 'platformatic', 'db', 'migrations', 'apply'], { cwd: projectDir })
|
|
224
|
+
spinner.succeed('...done!')
|
|
225
|
+
} catch (err) {
|
|
226
|
+
logger.trace({ err })
|
|
227
|
+
spinner.fail('...failed! Try again by running "platformatic db migrations apply"')
|
|
228
|
+
}
|
|
163
229
|
}
|
|
164
230
|
}
|
|
165
231
|
if (generatePlugin) {
|
|
@@ -173,8 +239,13 @@ const createPlatformaticDB = async (_args, opts) => {
|
|
|
173
239
|
|
|
174
240
|
if (generateTypes) {
|
|
175
241
|
const spinner = ora('Generating types...').start()
|
|
176
|
-
|
|
177
|
-
|
|
242
|
+
try {
|
|
243
|
+
await execa(pkgManager, ['exec', 'platformatic', 'db', 'types'], { cwd: projectDir })
|
|
244
|
+
spinner.succeed('...done!')
|
|
245
|
+
} catch (err) {
|
|
246
|
+
logger.trace({ err })
|
|
247
|
+
spinner.fail('...failed! Try again by running "platformatic db types"')
|
|
248
|
+
}
|
|
178
249
|
}
|
|
179
250
|
}
|
|
180
251
|
}
|
package/src/db/create-db.mjs
CHANGED
|
@@ -1,49 +1,37 @@
|
|
|
1
1
|
import { writeFile, mkdir, appendFile } from 'fs/promises'
|
|
2
2
|
import { join, relative, resolve } from 'path'
|
|
3
3
|
import { findDBConfigFile, isFileAccessible } from '../utils.mjs'
|
|
4
|
+
import { getTsConfig } from '../get-tsconfig.mjs'
|
|
4
5
|
|
|
5
6
|
const connectionStrings = {
|
|
6
|
-
postgres: 'postgres://postgres:postgres@
|
|
7
|
+
postgres: 'postgres://postgres:postgres@127.0.0.1:5432/postgres',
|
|
7
8
|
sqlite: 'sqlite://./db.sqlite',
|
|
8
|
-
mysql: 'mysql://root@
|
|
9
|
-
|
|
10
|
-
mariadb: 'mysql://root@localhost:3307/graph'
|
|
9
|
+
mysql: 'mysql://root@127.0.0.1:3306/platformatic',
|
|
10
|
+
mariadb: 'mysql://root@127.0.0.1:3306/platformatic'
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
const moviesMigrationDo =
|
|
13
|
+
const moviesMigrationDo = (database) => {
|
|
14
|
+
const key = {
|
|
15
|
+
postgres: 'SERIAL',
|
|
16
|
+
sqlite: 'INTEGER',
|
|
17
|
+
mysql: 'INTEGER UNSIGNED AUTO_INCREMENT',
|
|
18
|
+
mariadb: 'INTEGER UNSIGNED AUTO_INCREMENT'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return `
|
|
14
22
|
-- Add SQL in this file to create the database tables for your API
|
|
15
23
|
CREATE TABLE IF NOT EXISTS movies (
|
|
16
|
-
id
|
|
24
|
+
id ${key[database]} PRIMARY KEY,
|
|
17
25
|
title TEXT NOT NULL
|
|
18
26
|
);
|
|
19
27
|
`
|
|
28
|
+
}
|
|
20
29
|
|
|
21
30
|
const moviesMigrationUndo = `
|
|
22
31
|
-- Add SQL in this file to drop the database tables
|
|
23
32
|
DROP TABLE movies;
|
|
24
33
|
`
|
|
25
34
|
|
|
26
|
-
function getTsConfig (outDir) {
|
|
27
|
-
return {
|
|
28
|
-
compilerOptions: {
|
|
29
|
-
module: 'commonjs',
|
|
30
|
-
esModuleInterop: true,
|
|
31
|
-
target: 'es6',
|
|
32
|
-
sourceMap: true,
|
|
33
|
-
pretty: true,
|
|
34
|
-
noEmitOnError: true,
|
|
35
|
-
outDir
|
|
36
|
-
},
|
|
37
|
-
watchOptions: {
|
|
38
|
-
watchFile: 'fixedPollingInterval',
|
|
39
|
-
watchDirectory: 'fixedPollingInterval',
|
|
40
|
-
fallbackPolling: 'dynamicPriority',
|
|
41
|
-
synchronousWatchDirectory: true,
|
|
42
|
-
excludeDirectories: ['**/node_modules', outDir]
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
35
|
const getPluginName = (isTypescript) => isTypescript === true ? 'plugin.ts' : 'plugin.js'
|
|
48
36
|
const TS_OUT_DIR = 'dist'
|
|
49
37
|
|
|
@@ -61,6 +49,9 @@ function generateConfig (migrations, plugin, types, typescript, version) {
|
|
|
61
49
|
connectionString: '{DATABASE_URL}',
|
|
62
50
|
graphql: true,
|
|
63
51
|
openapi: true
|
|
52
|
+
},
|
|
53
|
+
watch: {
|
|
54
|
+
ignore: ['*.sqlite', '*.sqlite-journal']
|
|
64
55
|
}
|
|
65
56
|
}
|
|
66
57
|
|
|
@@ -83,20 +74,29 @@ function generateConfig (migrations, plugin, types, typescript, version) {
|
|
|
83
74
|
}
|
|
84
75
|
|
|
85
76
|
if (typescript === true) {
|
|
86
|
-
config.plugins.typescript =
|
|
77
|
+
config.plugins.typescript = '{PLT_TYPESCRIPT}'
|
|
87
78
|
}
|
|
88
79
|
|
|
89
80
|
return config
|
|
90
81
|
}
|
|
91
82
|
|
|
92
|
-
function generateEnv (hostname, port,
|
|
93
|
-
|
|
94
|
-
const env = `\
|
|
83
|
+
function generateEnv (hostname, port, connectionString, typescript) {
|
|
84
|
+
let env = `\
|
|
95
85
|
PLT_SERVER_HOSTNAME=${hostname}
|
|
96
86
|
PORT=${port}
|
|
97
87
|
PLT_SERVER_LOGGER_LEVEL=info
|
|
98
88
|
DATABASE_URL=${connectionString}
|
|
99
89
|
`
|
|
90
|
+
|
|
91
|
+
if (typescript === true) {
|
|
92
|
+
env += `\
|
|
93
|
+
|
|
94
|
+
# Set to false to disable automatic typescript compilation.
|
|
95
|
+
# Changing this setting is needed for production
|
|
96
|
+
PLT_TYPESCRIPT=true
|
|
97
|
+
`
|
|
98
|
+
}
|
|
99
|
+
|
|
100
100
|
return env
|
|
101
101
|
}
|
|
102
102
|
|
|
@@ -132,7 +132,12 @@ async function generatePluginWithTypesSupport (logger, currentDir, isTypescript)
|
|
|
132
132
|
logger.info(`Plugin file created at ${relative(currentDir, pluginPath)}`)
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
export function getConnectionString (database) {
|
|
136
|
+
return connectionStrings[database]
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export async function createDB ({ hostname, database = 'sqlite', port, migrations = 'migrations', plugin = true, types = true, typescript = false, connectionString }, logger, currentDir, version) {
|
|
140
|
+
connectionString = connectionString || getConnectionString(database)
|
|
136
141
|
const createMigrations = !!migrations // If we don't define a migrations folder, we don't create it
|
|
137
142
|
const accessibleConfigFilename = await findDBConfigFile(currentDir)
|
|
138
143
|
if (accessibleConfigFilename === undefined) {
|
|
@@ -140,7 +145,7 @@ async function createDB ({ hostname, database = 'sqlite', port, migrations = 'mi
|
|
|
140
145
|
await writeFile(join(currentDir, 'platformatic.db.json'), JSON.stringify(config, null, 2))
|
|
141
146
|
logger.info('Configuration file platformatic.db.json successfully created.')
|
|
142
147
|
|
|
143
|
-
const env = generateEnv(hostname, port,
|
|
148
|
+
const env = generateEnv(hostname, port, connectionString, typescript)
|
|
144
149
|
const envFileExists = await isFileAccessible('.env', currentDir)
|
|
145
150
|
await appendFile(join(currentDir, '.env'), env)
|
|
146
151
|
await writeFile(join(currentDir, '.env.sample'), env)
|
|
@@ -172,7 +177,7 @@ async function createDB ({ hostname, database = 'sqlite', port, migrations = 'mi
|
|
|
172
177
|
const isMigrationFileDoExists = await isFileAccessible(migrationFilePathDo)
|
|
173
178
|
const isMigrationFileUndoExists = await isFileAccessible(migrationFilePathUndo)
|
|
174
179
|
if (!isMigrationFileDoExists && createMigrations) {
|
|
175
|
-
await writeFile(migrationFilePathDo, moviesMigrationDo)
|
|
180
|
+
await writeFile(migrationFilePathDo, moviesMigrationDo(database))
|
|
176
181
|
logger.info(`Migration file ${migrationFileNameDo} successfully created.`)
|
|
177
182
|
if (!isMigrationFileUndoExists) {
|
|
178
183
|
await writeFile(migrationFilePathUndo, moviesMigrationUndo)
|
|
@@ -199,7 +204,7 @@ async function createDB ({ hostname, database = 'sqlite', port, migrations = 'mi
|
|
|
199
204
|
}
|
|
200
205
|
|
|
201
206
|
return {
|
|
202
|
-
DATABASE_URL:
|
|
207
|
+
DATABASE_URL: connectionString,
|
|
203
208
|
PLT_SERVER_LOGGER_LEVEL: 'info',
|
|
204
209
|
PORT: port,
|
|
205
210
|
PLT_SERVER_HOSTNAME: hostname
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function getTsConfig (outDir) {
|
|
2
|
+
return {
|
|
3
|
+
compilerOptions: {
|
|
4
|
+
module: 'commonjs',
|
|
5
|
+
esModuleInterop: true,
|
|
6
|
+
target: 'es2019',
|
|
7
|
+
sourceMap: true,
|
|
8
|
+
pretty: true,
|
|
9
|
+
noEmitOnError: true,
|
|
10
|
+
outDir
|
|
11
|
+
},
|
|
12
|
+
watchOptions: {
|
|
13
|
+
watchFile: 'fixedPollingInterval',
|
|
14
|
+
watchDirectory: 'fixedPollingInterval',
|
|
15
|
+
fallbackPolling: 'dynamicPriority',
|
|
16
|
+
synchronousWatchDirectory: true,
|
|
17
|
+
excludeDirectories: ['**/node_modules', outDir]
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
package/src/index.mjs
CHANGED
|
@@ -8,7 +8,9 @@ import createPlatformaticService from './service/create-service-cli.mjs'
|
|
|
8
8
|
import createPlatformaticComposer from './composer/create-composer-cli.mjs'
|
|
9
9
|
import { createPlatformaticRuntime, createRuntimeService } from './runtime/create-runtime-cli.mjs'
|
|
10
10
|
import commist from 'commist'
|
|
11
|
-
import { getUsername, getVersion, minimumSupportedNodeVersions, isCurrentVersionSupported, findRuntimeConfigFile } from './utils.mjs'
|
|
11
|
+
import { getUsername, getVersion, minimumSupportedNodeVersions, isCurrentVersionSupported, findRuntimeConfigFile, getDependencyVersion } from './utils.mjs'
|
|
12
|
+
import { createPackageJson } from './create-package-json.mjs'
|
|
13
|
+
import { createGitignore } from './create-gitignore.mjs'
|
|
12
14
|
|
|
13
15
|
export async function chooseKind (argv, opts = {}) {
|
|
14
16
|
const skip = opts.skip
|
|
@@ -101,3 +103,10 @@ const createPlatformatic = async (argv) => {
|
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
export default createPlatformatic
|
|
106
|
+
|
|
107
|
+
export {
|
|
108
|
+
createPackageJson,
|
|
109
|
+
createGitignore,
|
|
110
|
+
getVersion,
|
|
111
|
+
getDependencyVersion
|
|
112
|
+
}
|
|
@@ -2,7 +2,7 @@ import { getVersion, getDependencyVersion, isFileAccessible } from '../utils.mjs
|
|
|
2
2
|
import { createPackageJson } from '../create-package-json.mjs'
|
|
3
3
|
import { createGitignore } from '../create-gitignore.mjs'
|
|
4
4
|
import { getPkgManager } from '../get-pkg-manager.mjs'
|
|
5
|
-
import { join } from 'path'
|
|
5
|
+
import { join, relative } from 'path'
|
|
6
6
|
import inquirer from 'inquirer'
|
|
7
7
|
import { readFile, writeFile, mkdir } from 'fs/promises'
|
|
8
8
|
import pino from 'pino'
|
|
@@ -46,21 +46,24 @@ export async function createPlatformaticRuntime (_args) {
|
|
|
46
46
|
const pkgManager = getPkgManager()
|
|
47
47
|
|
|
48
48
|
const projectDir = await askDir(logger, '.')
|
|
49
|
-
|
|
49
|
+
|
|
50
|
+
// Create the project directory
|
|
51
|
+
await mkdir(projectDir, { recursive: true })
|
|
52
|
+
|
|
53
|
+
const baseServicesDir = join(relative(process.cwd(), projectDir), 'services')
|
|
54
|
+
const servicesDir = await askDir(logger, baseServicesDir, 'Where would you like to load your services from?')
|
|
50
55
|
|
|
51
56
|
const { runPackageManagerInstall } = await inquirer.prompt([
|
|
52
57
|
getRunPackageManagerInstall(pkgManager)
|
|
53
58
|
])
|
|
54
59
|
|
|
55
|
-
// Create the project directory
|
|
56
|
-
await mkdir(projectDir, { recursive: true })
|
|
57
60
|
await mkdir(servicesDir, { recursive: true })
|
|
58
61
|
|
|
59
62
|
const fastifyVersion = await getDependencyVersion('fastify')
|
|
60
63
|
|
|
61
64
|
// Create the package.json, notes that we don't have the option for TS (yet) so we don't generate
|
|
62
65
|
// the package.json with the TS build
|
|
63
|
-
await createPackageJson(
|
|
66
|
+
await createPackageJson(version, fastifyVersion, logger, projectDir, false)
|
|
64
67
|
await createGitignore(logger, projectDir)
|
|
65
68
|
await createReadme(logger, projectDir)
|
|
66
69
|
|
|
@@ -124,13 +127,24 @@ export async function createRuntimeService ({ servicesDir, names, logger }) {
|
|
|
124
127
|
type: 'input',
|
|
125
128
|
name: 'name',
|
|
126
129
|
message: 'What is the name of the service?',
|
|
127
|
-
default: generateName().dashed
|
|
130
|
+
default: generateName().dashed,
|
|
131
|
+
validate: (value) => {
|
|
132
|
+
if (value.length === 0) {
|
|
133
|
+
return 'Please enter a name'
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (value.includes(' ')) {
|
|
137
|
+
return 'Please enter a name without spaces'
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (names.includes(value)) {
|
|
141
|
+
return 'This name is already used, please choose another one.'
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return true
|
|
145
|
+
}
|
|
128
146
|
})
|
|
129
147
|
|
|
130
|
-
if (names.includes(name)) {
|
|
131
|
-
logger.warn('This name is already used, please choose another one.')
|
|
132
|
-
return false
|
|
133
|
-
}
|
|
134
148
|
names.push(name)
|
|
135
149
|
|
|
136
150
|
const serviceDir = join(servicesDir, name)
|
|
@@ -80,7 +80,7 @@ const createPlatformaticService = async (_args, opts = {}) => {
|
|
|
80
80
|
if (!opts.skipPackageJson) {
|
|
81
81
|
// Create the package.json, notes that we don't have the option for TS (yet) so we don't generate
|
|
82
82
|
// the package.json with the TS build
|
|
83
|
-
await createPackageJson(
|
|
83
|
+
await createPackageJson(version, fastifyVersion, logger, projectDir, false)
|
|
84
84
|
}
|
|
85
85
|
if (!opts.skipGitignore) {
|
|
86
86
|
await createGitignore(logger, projectDir)
|
|
@@ -2,13 +2,14 @@ import { writeFile, mkdir, 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
|
+
import { getTsConfig } from '../get-tsconfig.mjs'
|
|
5
6
|
|
|
6
7
|
const TS_OUT_DIR = 'dist'
|
|
7
8
|
|
|
8
9
|
function generateConfig (version, typescript) {
|
|
9
10
|
const plugins = {
|
|
10
11
|
paths: [
|
|
11
|
-
'./plugins',
|
|
12
|
+
{ path: './plugins', encapsulate: false },
|
|
12
13
|
'./routes'
|
|
13
14
|
]
|
|
14
15
|
}
|
|
@@ -29,31 +30,42 @@ function generateConfig (version, typescript) {
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
if (typescript === true) {
|
|
32
|
-
config.plugins.typescript =
|
|
33
|
+
config.plugins.typescript = '{PLT_TYPESCRIPT}'
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
return config
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
function generateEnv (hostname, port) {
|
|
39
|
-
|
|
39
|
+
function generateEnv (hostname, port, typescript) {
|
|
40
|
+
let env = `\
|
|
40
41
|
PLT_SERVER_HOSTNAME=${hostname}
|
|
41
42
|
PORT=${port}
|
|
42
43
|
PLT_SERVER_LOGGER_LEVEL=info
|
|
43
44
|
`
|
|
45
|
+
|
|
46
|
+
if (typescript === true) {
|
|
47
|
+
env += `\
|
|
48
|
+
|
|
49
|
+
# Set to false to disable automatic typescript compilation.
|
|
50
|
+
# Changing this setting is needed for production
|
|
51
|
+
PLT_TYPESCRIPT=true
|
|
52
|
+
`
|
|
53
|
+
}
|
|
54
|
+
|
|
44
55
|
return env
|
|
45
56
|
}
|
|
46
57
|
|
|
47
58
|
const JS_PLUGIN_WITH_TYPES_SUPPORT = `\
|
|
59
|
+
/// <reference types="@platformatic/service" />
|
|
48
60
|
'use strict'
|
|
49
61
|
/** @param {import('fastify').FastifyInstance} fastify */
|
|
50
62
|
module.exports = async function (fastify, opts) {
|
|
51
63
|
fastify.decorate('example', 'foobar')
|
|
52
64
|
}
|
|
53
|
-
module.exports[Symbol.for('skip-override')] = true
|
|
54
65
|
`
|
|
55
66
|
|
|
56
67
|
const TS_PLUGIN_WITH_TYPES_SUPPORT = `\
|
|
68
|
+
/// <reference types="@platformatic/service" />
|
|
57
69
|
import { FastifyInstance, FastifyPluginOptions } from 'fastify'
|
|
58
70
|
|
|
59
71
|
export default async function (fastify: FastifyInstance, opts: FastifyPluginOptions) {
|
|
@@ -62,6 +74,7 @@ export default async function (fastify: FastifyInstance, opts: FastifyPluginOpti
|
|
|
62
74
|
`
|
|
63
75
|
|
|
64
76
|
const JS_ROUTES_WITH_TYPES_SUPPORT = `\
|
|
77
|
+
/// <reference types="@platformatic/service" />
|
|
65
78
|
'use strict'
|
|
66
79
|
/** @param {import('fastify').FastifyInstance} fastify */
|
|
67
80
|
module.exports = async function (fastify, opts) {
|
|
@@ -72,6 +85,7 @@ module.exports = async function (fastify, opts) {
|
|
|
72
85
|
`
|
|
73
86
|
|
|
74
87
|
const TS_ROUTES_WITH_TYPES_SUPPORT = `\
|
|
88
|
+
/// <reference types="@platformatic/service" />
|
|
75
89
|
import { FastifyInstance, FastifyPluginOptions } from 'fastify'
|
|
76
90
|
|
|
77
91
|
declare module 'fastify' {
|
|
@@ -87,27 +101,6 @@ export default async function (fastify: FastifyInstance, opts: FastifyPluginOpti
|
|
|
87
101
|
}
|
|
88
102
|
`
|
|
89
103
|
|
|
90
|
-
function getTsConfig (outDir) {
|
|
91
|
-
return {
|
|
92
|
-
compilerOptions: {
|
|
93
|
-
module: 'commonjs',
|
|
94
|
-
esModuleInterop: true,
|
|
95
|
-
target: 'es6',
|
|
96
|
-
sourceMap: true,
|
|
97
|
-
pretty: true,
|
|
98
|
-
noEmitOnError: true,
|
|
99
|
-
outDir
|
|
100
|
-
},
|
|
101
|
-
watchOptions: {
|
|
102
|
-
watchFile: 'fixedPollingInterval',
|
|
103
|
-
watchDirectory: 'fixedPollingInterval',
|
|
104
|
-
fallbackPolling: 'dynamicPriority',
|
|
105
|
-
synchronousWatchDirectory: true,
|
|
106
|
-
excludeDirectories: ['**/node_modules', outDir]
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
104
|
async function generatePluginWithTypesSupport (logger, currentDir, isTypescript) {
|
|
112
105
|
await mkdir(join(currentDir, 'plugins'))
|
|
113
106
|
const pluginTemplate = isTypescript
|
|
@@ -144,7 +137,7 @@ async function createService ({ hostname, port, typescript = false }, logger, cu
|
|
|
144
137
|
await writeFile(join(currentDir, 'platformatic.service.json'), JSON.stringify(config, null, 2))
|
|
145
138
|
logger.info('Configuration file platformatic.service.json successfully created.')
|
|
146
139
|
|
|
147
|
-
const env = generateEnv(hostname, port)
|
|
140
|
+
const env = generateEnv(hostname, port, typescript)
|
|
148
141
|
const envFileExists = await isFileAccessible('.env', currentDir)
|
|
149
142
|
await appendFile(join(currentDir, '.env'), env)
|
|
150
143
|
await writeFile(join(currentDir, '.env.sample'), env)
|
package/src/utils.mjs
CHANGED
|
@@ -18,7 +18,10 @@ export async function isFileAccessible (filename, directory) {
|
|
|
18
18
|
return false
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Gets the username from git config or `whoami` command
|
|
23
|
+
* @returns string | null
|
|
24
|
+
*/
|
|
22
25
|
export const getUsername = async () => {
|
|
23
26
|
try {
|
|
24
27
|
const { stdout } = await execa('git', ['config', 'user.name'])
|
|
@@ -39,7 +42,10 @@ export const getUsername = async () => {
|
|
|
39
42
|
|
|
40
43
|
return null
|
|
41
44
|
}
|
|
42
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Get the platformatic package version from package.json
|
|
47
|
+
* @returns string
|
|
48
|
+
*/
|
|
43
49
|
/* c8 ignore next 4 */
|
|
44
50
|
export const getVersion = async () => {
|
|
45
51
|
const data = await readFile(desm.join(import.meta.url, '..', 'package.json'), 'utf8')
|
|
@@ -73,6 +79,11 @@ export const findServiceConfigFile = async (directory) => (ConfigManager.findCon
|
|
|
73
79
|
export const findComposerConfigFile = async (directory) => (ConfigManager.findConfigFile(directory, 'composer'))
|
|
74
80
|
export const findRuntimeConfigFile = async (directory) => (ConfigManager.findConfigFile(directory, 'runtime'))
|
|
75
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Gets the version of the specified dependency package from package.json
|
|
84
|
+
* @param {string} dependencyName
|
|
85
|
+
* @returns string
|
|
86
|
+
*/
|
|
76
87
|
export const getDependencyVersion = async (dependencyName) => {
|
|
77
88
|
const require = createRequire(import.meta.url)
|
|
78
89
|
const pathToPackageJson = join(dirname(require.resolve(dependencyName)), 'package.json')
|
|
@@ -23,12 +23,12 @@ afterEach(() => {
|
|
|
23
23
|
test('creates package.json file for db project', async ({ equal }) => {
|
|
24
24
|
const version = '1.2.3'
|
|
25
25
|
const fastifyVersion = '4.5.6'
|
|
26
|
-
await createPackageJson(
|
|
26
|
+
await createPackageJson(version, fastifyVersion, fakeLogger, tmpDir, false)
|
|
27
27
|
equal(log, `${join(tmpDir, 'package.json')} successfully created.`)
|
|
28
28
|
const accessible = await isFileAccessible(join(tmpDir, 'package.json'))
|
|
29
29
|
equal(accessible, true)
|
|
30
30
|
const packageJson = JSON.parse(readFileSync(join(tmpDir, 'package.json')))
|
|
31
|
-
equal(packageJson.scripts.start, 'platformatic
|
|
31
|
+
equal(packageJson.scripts.start, 'platformatic start')
|
|
32
32
|
equal(packageJson.scripts.build, undefined)
|
|
33
33
|
equal(packageJson.dependencies.platformatic, `^${version}`)
|
|
34
34
|
equal(packageJson.devDependencies.fastify, `^${fastifyVersion}`)
|
|
@@ -37,12 +37,12 @@ test('creates package.json file for db project', async ({ equal }) => {
|
|
|
37
37
|
test('creates package.json file for service project', async ({ equal }) => {
|
|
38
38
|
const version = '1.2.3'
|
|
39
39
|
const fastifyVersion = '4.5.6'
|
|
40
|
-
await createPackageJson(
|
|
40
|
+
await createPackageJson(version, fastifyVersion, fakeLogger, tmpDir, false)
|
|
41
41
|
equal(log, `${join(tmpDir, 'package.json')} successfully created.`)
|
|
42
42
|
const accessible = await isFileAccessible(join(tmpDir, 'package.json'))
|
|
43
43
|
equal(accessible, true)
|
|
44
44
|
const packageJson = JSON.parse(readFileSync(join(tmpDir, 'package.json')))
|
|
45
|
-
equal(packageJson.scripts.start, 'platformatic
|
|
45
|
+
equal(packageJson.scripts.start, 'platformatic start')
|
|
46
46
|
equal(packageJson.dependencies.platformatic, `^${version}`)
|
|
47
47
|
equal(packageJson.devDependencies.fastify, `^${fastifyVersion}`)
|
|
48
48
|
})
|
|
@@ -52,19 +52,19 @@ test('do not create package.json file because already present', async ({ equal }
|
|
|
52
52
|
const fastifyVersion = '4.5.6'
|
|
53
53
|
const packagejson = join(tmpDir, 'package.json')
|
|
54
54
|
writeFileSync(packagejson, 'TEST')
|
|
55
|
-
await createPackageJson(
|
|
55
|
+
await createPackageJson(version, fastifyVersion, fakeLogger, tmpDir, false)
|
|
56
56
|
equal(log, `${join(tmpDir, 'package.json')} found, skipping creation of package.json file.`)
|
|
57
57
|
})
|
|
58
58
|
|
|
59
59
|
test('creates package.json file with TS build', async ({ equal }) => {
|
|
60
60
|
const version = '1.2.3'
|
|
61
61
|
const fastifyVersion = '4.5.6'
|
|
62
|
-
await createPackageJson(
|
|
62
|
+
await createPackageJson(version, fastifyVersion, fakeLogger, tmpDir, true)
|
|
63
63
|
equal(log, `${join(tmpDir, 'package.json')} successfully created.`)
|
|
64
64
|
const accessible = await isFileAccessible(join(tmpDir, 'package.json'))
|
|
65
65
|
equal(accessible, true)
|
|
66
66
|
const packageJson = JSON.parse(readFileSync(join(tmpDir, 'package.json')))
|
|
67
|
-
equal(packageJson.scripts.start, 'npm run clean && platformatic
|
|
67
|
+
equal(packageJson.scripts.start, 'npm run clean && platformatic start')
|
|
68
68
|
equal(packageJson.scripts.clean, 'rm -fr ./dist')
|
|
69
69
|
equal(packageJson.scripts.build, 'npx tsc')
|
|
70
70
|
equal(packageJson.dependencies.platformatic, `^${version}`)
|
|
@@ -188,6 +188,7 @@ test('creates project with typescript', async ({ equal, same }) => {
|
|
|
188
188
|
equal(process.env.PLT_SERVER_HOSTNAME, 'myhost')
|
|
189
189
|
equal(process.env.PORT, '6666')
|
|
190
190
|
equal(process.env.DATABASE_URL, 'sqlite://./db.sqlite')
|
|
191
|
+
equal(process.env.PLT_TYPESCRIPT, 'true')
|
|
191
192
|
|
|
192
193
|
equal(db.graphql, true)
|
|
193
194
|
equal(db.openapi, true)
|
|
@@ -199,7 +200,7 @@ test('creates project with typescript', async ({ equal, same }) => {
|
|
|
199
200
|
equal(migrationFileUndo, moviesMigrationUndo)
|
|
200
201
|
|
|
201
202
|
same(plugins.paths, ['plugin.ts'])
|
|
202
|
-
equal(plugins.typescript,
|
|
203
|
+
equal(plugins.typescript, '{PLT_TYPESCRIPT}')
|
|
203
204
|
equal(await isFileAccessible(join(tmpDir, 'plugin.ts')), true)
|
|
204
205
|
equal(await isFileAccessible(join(tmpDir, 'tsconfig.json')), true)
|
|
205
206
|
})
|
|
@@ -59,8 +59,8 @@ test('creates service with typescript', async ({ equal, same, ok }) => {
|
|
|
59
59
|
equal(process.env.PLT_SERVER_HOSTNAME, 'myhost')
|
|
60
60
|
equal(process.env.PORT, '6666')
|
|
61
61
|
|
|
62
|
-
same(plugins.paths, ['./plugins', './routes'])
|
|
63
|
-
equal(plugins.typescript,
|
|
62
|
+
same(plugins.paths, [{ path: './plugins', encapsulate: false }, './routes'])
|
|
63
|
+
equal(plugins.typescript, '{PLT_TYPESCRIPT}')
|
|
64
64
|
|
|
65
65
|
ok(await isFileAccessible(join(tmpDir, 'tsconfig.json')))
|
|
66
66
|
ok(await isFileAccessible(join(tmpDir, 'plugins', 'example.ts')))
|
|
@@ -95,7 +95,7 @@ test('creates service with javascript', async ({ equal, same, ok }) => {
|
|
|
95
95
|
equal(process.env.PLT_SERVER_HOSTNAME, 'myhost')
|
|
96
96
|
equal(process.env.PORT, '6666')
|
|
97
97
|
|
|
98
|
-
same(plugins, { paths: ['./plugins', './routes'] })
|
|
98
|
+
same(plugins, { paths: [{ path: './plugins', encapsulate: false }, './routes'] })
|
|
99
99
|
ok(await isFileAccessible(join(tmpDir, 'plugins', 'example.js')))
|
|
100
100
|
ok(await isFileAccessible(join(tmpDir, 'routes', 'root.js')))
|
|
101
101
|
})
|