@ackee/create-node-app 1.0.1 → 2.0.2
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/AUTHORS +2 -1
- package/README.md +27 -19
- package/docs/development.md +42 -0
- package/lib/Bootstrap.js +106 -65
- package/lib/Bootstrap.js.map +1 -1
- package/lib/Builder.js +111 -0
- package/lib/Builder.js.map +1 -0
- package/lib/Files.js +21 -0
- package/lib/Files.js.map +1 -0
- package/lib/Logger.js +26 -8
- package/lib/Logger.js.map +1 -1
- package/lib/Mergers/ConfigMerger.js +22 -0
- package/lib/Mergers/ConfigMerger.js.map +1 -0
- package/lib/Mergers/ContainerMerger.js +172 -0
- package/lib/Mergers/ContainerMerger.js.map +1 -0
- package/lib/Mergers/EnvJsoncMerger.js +20 -0
- package/lib/Mergers/EnvJsoncMerger.js.map +1 -0
- package/lib/Mergers/Merger.js +36 -0
- package/lib/Mergers/Merger.js.map +1 -0
- package/lib/Mergers/PackageJsonMerger.js +36 -0
- package/lib/Mergers/PackageJsonMerger.js.map +1 -0
- package/lib/Npm.js +40 -12
- package/lib/Npm.js.map +1 -1
- package/lib/PackageJson.js +4 -4
- package/lib/PackageJson.js.map +1 -1
- package/lib/StarterLoader.js +86 -0
- package/lib/StarterLoader.js.map +1 -0
- package/package.json +10 -7
- package/src/Bootstrap.ts +123 -82
- package/src/Builder.ts +172 -0
- package/src/Files.ts +22 -0
- package/src/Logger.ts +26 -7
- package/src/Mergers/ConfigMerger.ts +28 -0
- package/src/Mergers/ContainerMerger.ts +241 -0
- package/src/Mergers/EnvJsoncMerger.ts +24 -0
- package/src/Mergers/Merger.ts +51 -0
- package/src/Mergers/PackageJsonMerger.ts +45 -0
- package/src/Npm.ts +60 -15
- package/src/PackageJson.ts +6 -4
- package/src/Starter.ts +2 -2
- package/src/StarterLoader.ts +148 -0
- package/starter/{cloudrun → _base}/.env.jsonc +1 -5
- package/starter/{cloudrun → _base}/.eslintrc.cjs +3 -2
- package/starter/_base/README.md +53 -0
- package/starter/_base/package.json +45 -0
- package/starter/{cloudrun → _base}/src/adapters/pino.logger.ts +1 -1
- package/starter/_base/src/config.ts +16 -0
- package/starter/{cloudrun-graphql → _base}/src/container.ts +3 -1
- package/starter/_base/src/index.ts +14 -0
- package/starter/{cloudrun → _base}/src/view/cli/README.md +2 -6
- package/starter/api/graphql/.env.jsonc +8 -0
- package/starter/{cloudrun-graphql → api/graphql}/.eslintrc.cjs +4 -5
- package/starter/api/graphql/node-app.jsonc +6 -0
- package/starter/api/graphql/package.json +36 -0
- package/starter/{cloudrun-graphql → api/graphql}/src/config.ts +0 -4
- package/starter/api/graphql/src/index.ts +11 -0
- package/starter/{cloudrun-graphql → api/graphql}/src/test/helloWorld.test.ts +14 -3
- package/starter/api/graphql/src/view/graphql/context-factory.ts +13 -0
- package/starter/{cloudrun-graphql → api/graphql}/src/view/server.ts +16 -6
- package/starter/api/rest/.env.jsonc +6 -0
- package/starter/api/rest/.eslintrc.cjs +8 -0
- package/starter/api/rest/node-app.jsonc +6 -0
- package/starter/api/rest/package.json +25 -0
- package/starter/{cloudrun → api/rest}/src/config.ts +0 -5
- package/starter/api/rest/src/container.ts +13 -0
- package/starter/{cloudrun → api/rest}/src/index.ts +4 -4
- package/starter/{cloudrun → api/rest}/src/test/health-check.test.ts +3 -5
- package/starter/{cloudrun → api/rest}/src/test/util/openapi-test.util.ts +3 -3
- package/starter/{cloudrun → api/rest}/src/view/rest/middleware/error-handler.ts +1 -1
- package/starter/{cloudrun → api/rest}/src/view/rest/routes.ts +1 -1
- package/starter/{cloudrun → api/rest}/src/view/rest/util/openapi.util.ts +22 -19
- package/starter/{cloudrun → api/rest}/src/view/server.ts +6 -4
- package/starter/infra/postgresql-knex/.env.jsonc +5 -0
- package/starter/{shared → infra/postgresql-knex}/docker-compose/docker-compose.yml +1 -1
- package/starter/infra/postgresql-knex/knexfile.ts +16 -0
- package/starter/infra/postgresql-knex/node-app.jsonc +6 -0
- package/starter/infra/postgresql-knex/package.json +13 -0
- package/starter/infra/postgresql-knex/src/adapters/knex.database.test.ts +21 -0
- package/starter/infra/postgresql-knex/src/adapters/knex.database.ts +14 -0
- package/starter/infra/postgresql-knex/src/adapters/repositories/migration.repository.ts +24 -0
- package/starter/infra/postgresql-knex/src/config.ts +14 -0
- package/starter/infra/postgresql-knex/src/container.ts +23 -0
- package/starter/infra/postgresql-knex/src/db/migration.template.ts +4 -0
- package/starter/infra/postgresql-knex/src/db/migrations/.gitkeep +0 -0
- package/starter/infra/postgresql-knex/src/db/seed.template.ts +3 -0
- package/starter/infra/postgresql-knex/src/db/seeds/.gitkeep +0 -0
- package/starter/infra/postgresql-knex/src/domain/ports/database.d.ts +4 -0
- package/starter/infra/postgresql-knex/src/domain/ports/repositories/migration.repository.d.ts +9 -0
- package/starter/infra/postgresql-knex/src/test/setup.ts +16 -0
- package/starter/{shared → pipeline/cloudrun-gitlab}/.gitlab-ci.yml +15 -6
- package/starter/pipeline/cloudrun-gitlab/node-app.jsonc +6 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/lib/Toolbelt.js +0 -102
- package/lib/Toolbelt.js.map +0 -1
- package/lib/cloudrun/CloudRunStarter.js +0 -127
- package/lib/cloudrun/CloudRunStarter.js.map +0 -1
- package/lib/cloudrun-graphql/GraphQLStarter.js +0 -118
- package/lib/cloudrun-graphql/GraphQLStarter.js.map +0 -1
- package/src/Toolbelt.ts +0 -132
- package/src/cloudrun/CloudRunStarter.ts +0 -182
- package/src/cloudrun-graphql/GraphQLStarter.ts +0 -182
- package/starter/cloudrun/README.md +0 -69
- package/starter/cloudrun/src/container.ts +0 -18
- package/starter/cloudrun/src/context.ts +0 -39
- package/starter/cloudrun/src/domain/errors/codes.ts +0 -9
- package/starter/cloudrun/src/domain/errors/errors.ts +0 -25
- package/starter/cloudrun/src/domain/ports/logger.d.ts +0 -21
- package/starter/cloudrun-graphql/.env.jsonc +0 -12
- package/starter/cloudrun-graphql/README.md +0 -53
- package/starter/cloudrun-graphql/src/adapters/pino.logger.ts +0 -44
- package/starter/cloudrun-graphql/src/index.ts +0 -11
- package/starter/shared/.gitignore_ +0 -5
- package/starter/shared/ci-branch-config/common.env +0 -7
- package/starter/shared/ci-branch-config/development.env +0 -7
- package/starter/shared/ci-branch-config/master.env +0 -7
- package/starter/shared/ci-branch-config/stage.env +0 -7
- package/starter/shared/docker-compose/docker-compose.override.yml +0 -5
- package/starter/shared/jest.config.js +0 -12
- /package/starter/{shared → _base}/.dockerignore +0 -0
- /package/starter/{cloudrun → _base}/.eslint.tsconfig.json +0 -0
- /package/starter/{shared → _base}/.mocha-junit-config.json +0 -0
- /package/starter/{shared → _base}/.mocharc.json +0 -0
- /package/starter/{shared → _base}/.nvmrc +0 -0
- /package/starter/{shared → _base}/Dockerfile +0 -0
- /package/starter/{shared → _base}/prettier.config.cjs +0 -0
- /package/starter/{cloudrun-graphql → _base}/src/context.ts +0 -0
- /package/starter/{cloudrun-graphql → _base}/src/domain/errors/codes.ts +0 -0
- /package/starter/{cloudrun-graphql → _base}/src/domain/errors/errors.ts +0 -0
- /package/starter/{cloudrun-graphql → _base}/src/domain/ports/logger.d.ts +0 -0
- /package/starter/{shared → _base}/src/test/setup.ts +0 -0
- /package/starter/{cloudrun → _base}/src/view/cli/cli.ts +0 -0
- /package/starter/{shared → _base}/tsconfig.json +0 -0
- /package/starter/{cloudrun-graphql → api/graphql}/.eslint.tsconfig.json +0 -0
- /package/starter/{cloudrun-graphql → api/graphql}/codegen.yml +0 -0
- /package/starter/{cloudrun-graphql → api/graphql}/src/view/controller.ts +0 -0
- /package/starter/{cloudrun-graphql → api/graphql}/src/view/graphql/resolvers/greeting.resolver.ts +0 -0
- /package/starter/{cloudrun-graphql → api/graphql}/src/view/graphql/resolvers.ts +0 -0
- /package/starter/{cloudrun-graphql → api/graphql}/src/view/graphql/schema/schema.graphql +0 -0
- /package/starter/{cloudrun-graphql → api/graphql}/src/view/graphql/schema.ts +0 -0
- /package/starter/{cloudrun → api/rest}/src/domain/health-check.service.ts +0 -0
- /package/starter/{cloudrun → api/rest}/src/view/cli/openapi/generate.ts +0 -0
- /package/starter/{cloudrun/src/view/rest/controller → api/rest/src/view/rest/controllers}/health-check.controller.ts +0 -0
- /package/starter/{cloudrun → api/rest}/src/view/rest/middleware/context-middleware.ts +0 -0
- /package/starter/{cloudrun → api/rest}/src/view/rest/middleware/request-logger.ts +0 -0
- /package/starter/{cloudrun → api/rest}/src/view/rest/request.d.ts +0 -0
- /package/starter/{cloudrun → api/rest}/src/view/rest/spec/openapi.yml +0 -0
- /package/starter/{shared → infra/postgresql-knex}/docker-compose/docker-compose-entrypoint.sh +0 -0
- /package/starter/{shared → infra/postgresql-knex}/docker-compose/docker-compose.ci.yml +0 -0
- /package/starter/{shared → infra/postgresql-knex}/docker-compose/docker-compose.local.yml +0 -0
package/src/Bootstrap.ts
CHANGED
|
@@ -1,99 +1,140 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Npm } from './Npm.js'
|
|
3
|
-
import { Toolbelt } from './Toolbelt.js'
|
|
1
|
+
import inquirer from 'inquirer'
|
|
4
2
|
import * as path from 'path'
|
|
5
|
-
import
|
|
6
|
-
import { GraphQLStarter } from './cloudrun-graphql/GraphQLStarter.js'
|
|
7
|
-
import { Path } from './types.js'
|
|
3
|
+
import * as fs from 'fs'
|
|
8
4
|
import yargs from 'yargs'
|
|
9
5
|
import { hideBin } from 'yargs/helpers'
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
6
|
+
import { Builder } from './Builder.js'
|
|
7
|
+
import { Logger } from './Logger.js'
|
|
8
|
+
import { Npm } from './Npm.js'
|
|
9
|
+
import { PackageJson } from './PackageJson.js'
|
|
10
|
+
import { LoadedStarter, StarterLoader } from './StarterLoader.js'
|
|
11
|
+
import { Path } from './types.js'
|
|
12
|
+
|
|
13
|
+
interface ParsedArgs {
|
|
14
|
+
dir: string
|
|
15
|
+
debug: boolean
|
|
16
|
+
force: boolean
|
|
17
|
+
projectName: string
|
|
18
|
+
[key: string]: unknown
|
|
19
|
+
}
|
|
12
20
|
|
|
13
21
|
export class Bootstrap {
|
|
14
|
-
protected
|
|
22
|
+
protected starterLoader = new StarterLoader()
|
|
15
23
|
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
24
|
+
private async askMissingOptions(parsedArgs: ParsedArgs) {
|
|
25
|
+
const starters: LoadedStarter[] = []
|
|
26
|
+
|
|
27
|
+
for (const module of this.starterLoader.getOptions()) {
|
|
28
|
+
const moduleOption = parsedArgs[module.name.toLowerCase()] as string
|
|
29
|
+
if (moduleOption) {
|
|
30
|
+
if (moduleOption !== 'none') {
|
|
31
|
+
starters.push(this.starterLoader.getStarter(moduleOption))
|
|
32
|
+
}
|
|
33
|
+
continue
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const answer = await inquirer.prompt<{ starter: string }>({
|
|
37
|
+
type: 'list',
|
|
20
38
|
name: 'starter',
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
description: 'Which template to setup (required)',
|
|
24
|
-
choices: this.starters.map(starter => starter.name),
|
|
25
|
-
})
|
|
26
|
-
.option('dir', {
|
|
27
|
-
type: 'string',
|
|
28
|
-
alias: 'd',
|
|
29
|
-
default: './node-app',
|
|
30
|
-
description: 'Destination directory',
|
|
39
|
+
message: `Which ${module.name} would you like to use?`,
|
|
40
|
+
choices: [...module.starters, 'none'],
|
|
31
41
|
})
|
|
32
|
-
.option('project-name', {
|
|
33
|
-
type: 'string',
|
|
34
|
-
alias: 'n',
|
|
35
|
-
default: 'node-app',
|
|
36
|
-
description: 'Google Cloud project name',
|
|
37
|
-
})
|
|
38
|
-
.option('force', {
|
|
39
|
-
type: 'boolean',
|
|
40
|
-
alias: 'f',
|
|
41
|
-
default: false,
|
|
42
|
-
description:
|
|
43
|
-
"Overwrite existing destination directory if it's not empty",
|
|
44
|
-
})
|
|
45
|
-
.version('1.0.0')
|
|
46
|
-
.help()
|
|
47
42
|
|
|
48
|
-
|
|
49
|
-
|
|
43
|
+
if (answer.starter !== 'none') {
|
|
44
|
+
starters.push(this.starterLoader.getStarter(answer.starter))
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return starters
|
|
48
|
+
}
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
public async runCLI(args: string[]) {
|
|
51
|
+
try {
|
|
52
|
+
let cli = yargs(hideBin(args))
|
|
53
|
+
.usage('create-node-app [options]')
|
|
54
|
+
.option('dir', {
|
|
55
|
+
type: 'string',
|
|
56
|
+
alias: 'd',
|
|
57
|
+
default: './node-app',
|
|
58
|
+
description: 'Destination directory',
|
|
59
|
+
})
|
|
60
|
+
.option('debug', {
|
|
61
|
+
type: 'boolean',
|
|
62
|
+
alias: 'D',
|
|
63
|
+
default: false,
|
|
64
|
+
description: 'Enables debug logs',
|
|
65
|
+
})
|
|
66
|
+
.option('project-name', {
|
|
67
|
+
type: 'string',
|
|
68
|
+
alias: 'n',
|
|
69
|
+
default: 'node-app',
|
|
70
|
+
description: 'Google Cloud project name',
|
|
71
|
+
})
|
|
72
|
+
.option('force', {
|
|
73
|
+
type: 'boolean',
|
|
74
|
+
alias: 'f',
|
|
75
|
+
default: false,
|
|
76
|
+
description:
|
|
77
|
+
"Overwrite existing destination directory if it's not empty",
|
|
78
|
+
})
|
|
53
79
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
80
|
+
const starterOptions = this.starterLoader.getOptions()
|
|
81
|
+
for (const module of starterOptions) {
|
|
82
|
+
cli = cli.option(module.name.toLowerCase(), {
|
|
83
|
+
type: 'string',
|
|
84
|
+
choices: ['none', ...module.starters],
|
|
85
|
+
description: `Selects ${module.name}`,
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
cli = cli
|
|
90
|
+
.version()
|
|
91
|
+
.help()
|
|
92
|
+
.check(argv => {
|
|
93
|
+
for (const [key, val] of Object.entries(argv)) {
|
|
94
|
+
if (Array.isArray(val) && key !== '_') {
|
|
95
|
+
throw new Error(`Option --${key} specified multiple times`)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return true
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
const parsedArgs = cli.parseSync()
|
|
102
|
+
|
|
103
|
+
const destination = path.normalize(parsedArgs.dir) as Path
|
|
59
104
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
process.exit(
|
|
105
|
+
const logger = new Logger(parsedArgs.debug)
|
|
106
|
+
const npm = new Npm({ dir: destination, logger })
|
|
107
|
+
const packageJson = new PackageJson(npm, logger)
|
|
108
|
+
|
|
109
|
+
if (fs.existsSync(destination) && !parsedArgs.force) {
|
|
110
|
+
const answer = await inquirer.prompt<{ force: boolean }>({
|
|
111
|
+
type: 'confirm',
|
|
112
|
+
name: 'force',
|
|
113
|
+
message: `Destination directory "${destination}" already exists. Do you want to overwrite everything in it?`,
|
|
114
|
+
})
|
|
115
|
+
if (!answer.force) {
|
|
116
|
+
process.exit(0)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const starters = await this.askMissingOptions(parsedArgs)
|
|
121
|
+
|
|
122
|
+
const builder = new Builder({
|
|
123
|
+
npm,
|
|
124
|
+
logger,
|
|
125
|
+
packageJson,
|
|
126
|
+
starters,
|
|
127
|
+
destination: destination,
|
|
128
|
+
projectName: parsedArgs.projectName,
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
await builder.build()
|
|
132
|
+
} catch (error) {
|
|
133
|
+
if (error instanceof Error && error.name === 'ExitPromptError') {
|
|
134
|
+
process.exit(0)
|
|
90
135
|
} else {
|
|
91
|
-
|
|
136
|
+
throw error
|
|
92
137
|
}
|
|
93
138
|
}
|
|
94
|
-
|
|
95
|
-
toolbelt.mkdir(destination, { overwrite: parsedArgs.force })
|
|
96
|
-
toolbelt.npm.init()
|
|
97
|
-
starter.install()
|
|
98
139
|
}
|
|
99
140
|
}
|
package/src/Builder.ts
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import glob from 'fast-glob'
|
|
2
|
+
import * as fs from 'fs/promises'
|
|
3
|
+
import * as path from 'path'
|
|
4
|
+
import { Logger } from './Logger.js'
|
|
5
|
+
import { Npm } from './Npm.js'
|
|
6
|
+
import { PackageJson } from './PackageJson.js'
|
|
7
|
+
import { LoadedStarter, StarterConfig } from './StarterLoader.js'
|
|
8
|
+
import { Merger } from './Mergers/Merger.js'
|
|
9
|
+
import { PackageJsonMerger } from './Mergers/PackageJsonMerger.js'
|
|
10
|
+
import { EnvJsoncMerger } from './Mergers/EnvJsoncMerger.js'
|
|
11
|
+
import { ConfigMerger } from './Mergers/ConfigMerger.js'
|
|
12
|
+
import { ContainerMerger } from './Mergers/ContainerMerger.js'
|
|
13
|
+
import { Files } from './Files.js'
|
|
14
|
+
|
|
15
|
+
export class Builder {
|
|
16
|
+
public static readonly BASE_STARTER_DIR = path.normalize(
|
|
17
|
+
path.join(import.meta.dirname, '..', 'starter', '_base')
|
|
18
|
+
)
|
|
19
|
+
public static readonly IGNORED_FILES = ['node-app.jsonc']
|
|
20
|
+
|
|
21
|
+
public readonly npm: Npm
|
|
22
|
+
protected readonly logger: Logger
|
|
23
|
+
protected readonly starters: LoadedStarter[]
|
|
24
|
+
protected readonly fileMergers: Merger[]
|
|
25
|
+
protected readonly destination: string
|
|
26
|
+
protected readonly projectName: string
|
|
27
|
+
protected readonly replacements: Record<string, string>
|
|
28
|
+
|
|
29
|
+
constructor(params: {
|
|
30
|
+
npm: Npm
|
|
31
|
+
logger: Logger
|
|
32
|
+
packageJson: PackageJson
|
|
33
|
+
starters: LoadedStarter[]
|
|
34
|
+
destination: string
|
|
35
|
+
projectName: string
|
|
36
|
+
}) {
|
|
37
|
+
this.npm = params.npm
|
|
38
|
+
this.logger = params.logger
|
|
39
|
+
this.starters = params.starters
|
|
40
|
+
this.destination = params.destination
|
|
41
|
+
this.projectName = params.projectName
|
|
42
|
+
this.replacements = {
|
|
43
|
+
'{{PROJECT_NAME}}': this.projectName,
|
|
44
|
+
}
|
|
45
|
+
this.fileMergers = [
|
|
46
|
+
new PackageJsonMerger(this.projectName, this.destination, 'package.json'),
|
|
47
|
+
new EnvJsoncMerger(this.destination, '.env.jsonc'),
|
|
48
|
+
new ConfigMerger(this.destination, 'src/config.ts'),
|
|
49
|
+
new ContainerMerger(this.destination, 'src/container.ts'),
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
protected async prepareFolder() {
|
|
54
|
+
if (await Files.existsAndIsDir(this.destination)) {
|
|
55
|
+
await fs.rm(this.destination, { recursive: true })
|
|
56
|
+
}
|
|
57
|
+
await fs.mkdir(this.destination, { recursive: true })
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public async build() {
|
|
61
|
+
try {
|
|
62
|
+
await this.logger.loader(`Preparing clean folder`, this.prepareFolder())
|
|
63
|
+
|
|
64
|
+
await this.logger.loader(
|
|
65
|
+
`Preparing folder structure`,
|
|
66
|
+
this.buildStarter(Builder.BASE_STARTER_DIR)
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
for (const starter of this.starters) {
|
|
70
|
+
await this.logger.loader(
|
|
71
|
+
`Adding ${starter.config.name} ${starter.config.module}`,
|
|
72
|
+
this.buildStarter(starter.path, starter.config)
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
await this.logger.loader(`npm install`, this.npm.run(['install']))
|
|
77
|
+
|
|
78
|
+
const prebuildScripts: Array<string[]> = this.starters
|
|
79
|
+
.map(starter => starter.config.prebuild)
|
|
80
|
+
.filter(script => script !== undefined)
|
|
81
|
+
|
|
82
|
+
for (const script of prebuildScripts) {
|
|
83
|
+
await this.logger.loader(
|
|
84
|
+
`npm run ${script.join(' ')}`,
|
|
85
|
+
this.npm.run(['run', ...script])
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
await this.logger.loader(`npm run build`, this.npm.run(['run', 'build']))
|
|
90
|
+
|
|
91
|
+
this.logger.info(
|
|
92
|
+
`Your app is ready in ${path.relative(
|
|
93
|
+
process.cwd(),
|
|
94
|
+
this.destination
|
|
95
|
+
)}! 🚀`
|
|
96
|
+
)
|
|
97
|
+
} catch (error) {
|
|
98
|
+
this.logger.error(error)
|
|
99
|
+
process.exit(1)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
protected async buildStarter(starterDir: string, config?: StarterConfig) {
|
|
104
|
+
const destDir = path.normalize(path.resolve(this.destination))
|
|
105
|
+
const files = await glob(`${starterDir}/*`, {
|
|
106
|
+
cwd: starterDir,
|
|
107
|
+
dot: true,
|
|
108
|
+
onlyFiles: false,
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
const ignoredFiles = Builder.IGNORED_FILES.map(file =>
|
|
112
|
+
path.join(starterDir, file)
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
const mergedFiles = await Promise.all(
|
|
116
|
+
this.fileMergers.map(async merger => {
|
|
117
|
+
return {
|
|
118
|
+
path: merger.getDestPath(),
|
|
119
|
+
content: await merger.merge(starterDir),
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
await Promise.all(
|
|
125
|
+
files.map(async filePath => {
|
|
126
|
+
if (ignoredFiles.includes(filePath)) {
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
const destFilePath = path.join(destDir, path.basename(filePath))
|
|
130
|
+
if (await Files.existsAndIsDir(filePath)) {
|
|
131
|
+
await fs.cp(filePath, destFilePath, { recursive: true })
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
await fs.cp(filePath, destFilePath)
|
|
135
|
+
})
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
await Promise.all(
|
|
139
|
+
mergedFiles.map(async ({ path, content }) => fs.writeFile(path, content))
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
if (config?.replace) {
|
|
143
|
+
await Promise.all(
|
|
144
|
+
config.replace.map(async filePath => this.replaceInFile(filePath))
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
public async replaceInFile(filePath: string) {
|
|
150
|
+
filePath = path.normalize(path.join(this.destination, filePath))
|
|
151
|
+
let content = await Files.readUtf8File(filePath)
|
|
152
|
+
content = Object.keys(this.replacements).reduce((acc, key) => {
|
|
153
|
+
return acc.replaceAll(key, this.replacements[key])
|
|
154
|
+
}, content)
|
|
155
|
+
return fs.writeFile(filePath, content)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
public async symlink(linkName: string, linkedFile: string) {
|
|
159
|
+
linkName = path.normalize(linkName)
|
|
160
|
+
linkedFile = path.normalize(linkedFile)
|
|
161
|
+
this.logger.info(`> ln -s ${linkName} ${linkedFile}`)
|
|
162
|
+
try {
|
|
163
|
+
await fs.symlink(linkedFile, linkName)
|
|
164
|
+
} catch (error) {
|
|
165
|
+
if ('code' in error && error.code === 'EEXIST') {
|
|
166
|
+
// OK
|
|
167
|
+
} else {
|
|
168
|
+
throw error
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
package/src/Files.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as fsp from 'fs/promises'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
|
|
4
|
+
export class Files {
|
|
5
|
+
public static async exists(filepath: string) {
|
|
6
|
+
const stat = await fsp.stat(filepath).catch(() => undefined)
|
|
7
|
+
return Boolean(stat)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
public static async existsAndIsDir(filepath: string) {
|
|
11
|
+
const stat = await fsp.stat(filepath).catch(() => undefined)
|
|
12
|
+
return Boolean(stat?.isDirectory())
|
|
13
|
+
}
|
|
14
|
+
public static async readUtf8File(filepath: string) {
|
|
15
|
+
return fsp.readFile(filepath, 'utf8')
|
|
16
|
+
}
|
|
17
|
+
public static isInSameTree(tree: string, filepath: string) {
|
|
18
|
+
const file = path.normalize(filepath)
|
|
19
|
+
const fileTree = path.normalize(tree)
|
|
20
|
+
return file.startsWith(fileTree)
|
|
21
|
+
}
|
|
22
|
+
}
|
package/src/Logger.ts
CHANGED
|
@@ -1,11 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { oraPromise } from 'ora'
|
|
2
|
+
|
|
3
|
+
export class Logger {
|
|
4
|
+
constructor(public readonly enableDebug: boolean = false) {}
|
|
5
|
+
|
|
6
|
+
info(message: string) {
|
|
3
7
|
console.log(message)
|
|
4
|
-
}
|
|
5
|
-
verbose
|
|
8
|
+
}
|
|
9
|
+
verbose(message: string) {
|
|
6
10
|
console.log(message)
|
|
7
|
-
}
|
|
8
|
-
error
|
|
11
|
+
}
|
|
12
|
+
error(message: string) {
|
|
9
13
|
console.log(message)
|
|
10
|
-
}
|
|
14
|
+
}
|
|
15
|
+
debug(message: string) {
|
|
16
|
+
if (this.enableDebug) {
|
|
17
|
+
console.log(message)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
loader(message: string, promise: Promise<any>) {
|
|
21
|
+
if (this.enableDebug) {
|
|
22
|
+
this.info(message)
|
|
23
|
+
return promise
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return oraPromise(promise, {
|
|
27
|
+
text: message,
|
|
28
|
+
})
|
|
29
|
+
}
|
|
11
30
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Files } from '../Files.js'
|
|
2
|
+
import { Merger } from './Merger.js'
|
|
3
|
+
|
|
4
|
+
export class ConfigMerger extends Merger {
|
|
5
|
+
name = 'Config'
|
|
6
|
+
|
|
7
|
+
async merge(originDir: string): Promise<string> {
|
|
8
|
+
const content = await this.getWhichExistsOrNull(originDir)
|
|
9
|
+
if (content) {
|
|
10
|
+
return content
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { originPath, destPath } = this.getPaths(originDir)
|
|
14
|
+
|
|
15
|
+
const [destTsConfig, originTsConfig] = await Promise.all([
|
|
16
|
+
Files.readUtf8File(destPath),
|
|
17
|
+
Files.readUtf8File(originPath),
|
|
18
|
+
])
|
|
19
|
+
const configContent = /configSchema = {\n(.*?)\n}\n/s.exec(
|
|
20
|
+
originTsConfig
|
|
21
|
+
)?.[1]
|
|
22
|
+
|
|
23
|
+
return destTsConfig.replace(
|
|
24
|
+
'\n}\n',
|
|
25
|
+
configContent ? `${configContent}\n}\n` : '}\n'
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
}
|