@lakutata/cli 2.1.0-alpha.4 → 2.1.0-alpha.5

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.
Files changed (41) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/CLIApp.d.ts +3 -0
  3. package/dist/CLIApp.d.ts.map +1 -0
  4. package/dist/CLIApp.js +75 -0
  5. package/dist/controllers/CommandLineController.d.ts +20 -0
  6. package/dist/controllers/CommandLineController.d.ts.map +1 -0
  7. package/dist/controllers/CommandLineController.js +61 -0
  8. package/dist/lib/ProjectTypeConfig.d.ts +8 -0
  9. package/dist/lib/ProjectTypeConfig.d.ts.map +1 -0
  10. package/{src/lib/ProjectTypeConfig.ts → dist/lib/ProjectTypeConfig.js} +5 -8
  11. package/dist/lib/SpecialChar.d.ts +9 -0
  12. package/dist/lib/SpecialChar.d.ts.map +1 -0
  13. package/dist/lib/SpecialChar.js +11 -0
  14. package/dist/lib/components/DeGitPuller.d.ts +19 -0
  15. package/dist/lib/components/DeGitPuller.d.ts.map +1 -0
  16. package/dist/lib/components/DeGitPuller.js +65 -0
  17. package/dist/lib/components/Spinner.d.ts +23 -0
  18. package/dist/lib/components/Spinner.d.ts.map +1 -0
  19. package/dist/lib/components/Spinner.js +61 -0
  20. package/dist/lib/providers/Creator.d.ts +38 -0
  21. package/dist/lib/providers/Creator.d.ts.map +1 -0
  22. package/dist/lib/providers/Creator.js +142 -0
  23. package/dist/lib/providers/Information.d.ts +91 -0
  24. package/dist/lib/providers/Information.d.ts.map +1 -0
  25. package/dist/lib/providers/Information.js +222 -0
  26. package/dist/options/CreateProjectOptions.d.ts +15 -0
  27. package/dist/options/CreateProjectOptions.d.ts.map +1 -0
  28. package/dist/options/CreateProjectOptions.js +86 -0
  29. package/dist/options/LakutataInfoOptions.d.ts +4 -0
  30. package/dist/options/LakutataInfoOptions.d.ts.map +1 -0
  31. package/dist/options/LakutataInfoOptions.js +7 -0
  32. package/package.json +5 -2
  33. package/src/CLIApp.ts +0 -86
  34. package/src/controllers/CommandLineController.ts +0 -34
  35. package/src/lib/SpecialChar.ts +0 -8
  36. package/src/lib/components/DeGitPuller.ts +0 -41
  37. package/src/lib/components/Spinner.ts +0 -48
  38. package/src/lib/providers/Creator.ts +0 -125
  39. package/src/lib/providers/Information.ts +0 -221
  40. package/src/options/CreateProjectOptions.ts +0 -82
  41. package/src/options/LakutataInfoOptions.ts +0 -4
@@ -1,48 +0,0 @@
1
- import {type Spinner as CliSpinner, dots} from 'cli-spinners'
2
- import {Configurable, Singleton} from 'lakutata/decorator/di'
3
- import {Component} from 'lakutata'
4
- import {As} from 'lakutata/helper'
5
-
6
- @Singleton()
7
- export class Spinner extends Component {
8
-
9
- @Configurable()
10
- protected readonly style: CliSpinner = dots
11
-
12
- protected spinnerInterval: NodeJS.Timeout | null = null
13
-
14
- protected logUpdate: any
15
-
16
- /**
17
- * Initializer
18
- * @protected
19
- */
20
- protected async init(): Promise<void> {
21
- this.logUpdate = As(await import('log-update'))
22
- }
23
-
24
- /**
25
- * Start spinner
26
- * @param description
27
- */
28
- public start(description?: string | (() => string)): void {
29
- this.stop()
30
- let i: number = 0
31
- this.spinnerInterval = setInterval((): void => {
32
- const {frames} = this.style
33
- const text: string = description ? `${frames[i = ++i % frames.length]} ${typeof description === 'function' ? description() : description}` : frames[i = ++i % frames.length]
34
- this.logUpdate(text)
35
- }, dots.interval)
36
- }
37
-
38
- /**
39
- * Stop spinner
40
- */
41
- public stop(): void {
42
- if (this.spinnerInterval) {
43
- clearInterval(this.spinnerInterval)
44
- this.spinnerInterval = null
45
- this.logUpdate.clear()
46
- }
47
- }
48
- }
@@ -1,125 +0,0 @@
1
- import {Information} from './Information.js'
2
- import {DeGitPuller} from '../components/DeGitPuller.js'
3
- import {Spinner} from '../components/Spinner.js'
4
- import {CreateProjectOptions} from '../../options/CreateProjectOptions.js'
5
- import {ProjectTypeConfig} from '../ProjectTypeConfig.js'
6
- import path from 'node:path'
7
- import {mkdir, readdir, stat} from 'node:fs/promises'
8
- import {Stats} from 'node:fs'
9
- import {charCheck, charCross} from '../SpecialChar.js'
10
- import {Application, Provider} from 'lakutata'
11
- import {Inject} from 'lakutata/decorator/di'
12
- import {Logger} from 'lakutata/com/logger'
13
- import {IsExists} from 'lakutata/helper'
14
- import {Accept} from 'lakutata/decorator/dto'
15
- import ansis from 'ansis'
16
- import CLITable from 'cli-table3'
17
-
18
- export class Creator extends Provider {
19
-
20
- @Inject(Application)
21
- protected readonly app: Application
22
-
23
- @Inject('log')
24
- protected readonly log: Logger
25
-
26
- @Inject('spinner')
27
- protected readonly spinner: Spinner
28
-
29
- @Inject('puller')
30
- protected readonly puller: DeGitPuller
31
-
32
- @Inject('info')
33
- protected readonly frameworkInfo: Information
34
-
35
- /**
36
- * Check if the target path exists
37
- * @param targetDirectory
38
- * @param initOnly
39
- * @protected
40
- */
41
- protected async checkTargetPathExistence(targetDirectory: string, initOnly: boolean): Promise<void> {
42
- const exists: boolean = await IsExists(targetDirectory)
43
- if (!exists && initOnly) {
44
- this.log.error(`${charCross} The target path does not exist.`)
45
- return this.app.exit(1)
46
- }
47
- await mkdir(targetDirectory, {recursive: true})
48
- this.log.info(`${charCheck} The target path does not exist.`)
49
- }
50
-
51
- /**
52
- * Check target path is a valid directory
53
- * @param targetDirectory
54
- * @protected
55
- */
56
- protected async checkTargetPathIsDirectory(targetDirectory: string): Promise<void> {
57
- const targetInfo: Stats = await stat(targetDirectory)
58
- if (!targetInfo.isDirectory()) {
59
- this.log.error(`${charCross} The target path is not a valid directory.`)
60
- return this.app.exit(1)
61
- }
62
- this.log.info(`${charCheck} The target path is a valid directory.`)
63
- }
64
-
65
- /**
66
- * Check target directory is empty, if the target directory is not empty, throw error and exit
67
- * @param targetDirectory
68
- * @protected
69
- */
70
- protected async checkTargetDirectoryIsEmpty(targetDirectory: string): Promise<void> {
71
- const files: string[] = await readdir(targetDirectory)
72
- if (files.length) {
73
- this.log.error(`${charCross} The target directory is not empty.`)
74
- return this.app.exit(1)
75
- }
76
- this.log.info(`${charCheck} The target directory is empty.`)
77
- }
78
-
79
- /**
80
- * Exec create
81
- * @param options
82
- */
83
- @Accept(CreateProjectOptions.required())
84
- public async create(options: CreateProjectOptions): Promise<void> {
85
- const appName: string = options.name
86
- const appId: string = options.id
87
- const appDescription: string = options.description
88
- const packageName: string = appId
89
- const authorName: string = options.author
90
- const licenseName: string = options.license
91
- const appType: string = options.type
92
- const targetPath: string = options.initOnly ? path.resolve(options.path) : path.resolve(options.path, options.name)
93
- const {branch} = ProjectTypeConfig[appType]
94
- const table: CLITable.Table = new CLITable()
95
- table.push(
96
- [{content: ansis.bold.cyan('Project Creation Information'), colSpan: 2, hAlign: 'center'}],
97
- [ansis.blue('APP ID & Project Name'), appId],
98
- [ansis.blue('APP Name'), appName],
99
- [ansis.blue('APP Description'), appDescription],
100
- [ansis.blue('Project Create Target Path'), targetPath],
101
- [ansis.blue('Project Create Mode'), options.initOnly ? ansis.yellow('Initialize project in an existing directory') : ansis.green('Create a new folder for the project')],
102
- [ansis.blue('Project Author Name'), authorName],
103
- [ansis.blue('Project License'), licenseName],
104
- [ansis.blue('Project Template Branch'), this.puller.getGitSource(branch)]
105
- )
106
- console.log(table.toString())
107
- await new Promise<void>(resolve => {
108
- let timeLeft: number = 15
109
- const interval: NodeJS.Timeout = setInterval((): void => {
110
- timeLeft -= 1
111
- if (!timeLeft) {
112
- clearInterval(interval)
113
- this.spinner.stop()
114
- return resolve()
115
- }
116
- }, 1000)
117
- this.spinner.start((): string => `Please confirm the project creation information; the creation process will commence in ${timeLeft} seconds.`)
118
- })
119
- await this.checkTargetPathExistence(targetPath, options.initOnly)
120
- await this.checkTargetPathIsDirectory(targetPath)
121
- await this.checkTargetDirectoryIsEmpty(targetPath)
122
- this.log.info('Begin project creation')
123
- //TODO
124
- }
125
- }
@@ -1,221 +0,0 @@
1
- import ansis from 'ansis'
2
- import {dirname, resolve} from 'node:path'
3
- import {readFile} from 'node:fs/promises'
4
- import {DTO, Provider, Time} from 'lakutata'
5
- import {Configurable} from 'lakutata/decorator/di'
6
- import {DevNull} from 'lakutata/helper'
7
-
8
- export class Information extends Provider {
9
-
10
- @Configurable(DTO.String().required())
11
- protected readonly name: string
12
-
13
- @Configurable(DTO.String().required())
14
- protected readonly version: string
15
-
16
- @Configurable(DTO.String().required())
17
- protected readonly description: string
18
-
19
- @Configurable(DTO.String().required())
20
- protected readonly license: string
21
-
22
- @Configurable(DTO.String().required())
23
- protected readonly currentDirectory: string
24
-
25
- @Configurable(DTO.String().required())
26
- protected readonly workingDirectory: string
27
-
28
- protected packageDirectory: (options?: any) => Promise<string | undefined>
29
-
30
- protected installPath: string
31
-
32
- protected projectRoot: string | null
33
-
34
- /**
35
- * Initializer
36
- * @protected
37
- */
38
- protected async init(): Promise<void> {
39
- const {packageDirectory} = await import('pkg-dir')
40
- this.packageDirectory = packageDirectory
41
- const installPath: string | undefined = await packageDirectory({cwd: this.currentDirectory})
42
- this.installPath = installPath ? installPath : 'UNKNOWN'
43
-
44
- const projectRoot: string | null = await this.findProjectRoot(this.workingDirectory)
45
- if (projectRoot) {
46
- const packageJsonPath: string = resolve(projectRoot, './package.json')
47
- try {
48
- const packageJson = JSON.parse(await readFile(packageJsonPath, {encoding: 'utf-8'}))
49
- const dependenciesKeyRegExp: RegExp = new RegExp('dependencies'.toUpperCase())
50
- Object.keys(packageJson).forEach((key: string): void => {
51
- if (dependenciesKeyRegExp.test(key.toUpperCase())) {
52
- Object.keys(packageJson[key]).forEach((dependencyName: string) => {
53
- if (dependencyName === this.name) {
54
- this.projectRoot = projectRoot
55
- }
56
- })
57
- }
58
- })
59
- } catch (e) {
60
- DevNull(e)
61
- }
62
- }
63
- }
64
-
65
- /**
66
- * To find the root path of the project where the working directory is located
67
- * @param path
68
- * @private
69
- */
70
- private async findProjectRoot(path: string): Promise<string | null> {
71
- let localRootPath: string | null = null
72
- while (true) {
73
- const _localRootPath: string | null = await this.findLocalRoot(localRootPath ? dirname(localRootPath) : path)
74
- if (!_localRootPath)
75
- break
76
- else
77
- localRootPath = _localRootPath
78
- }
79
- return localRootPath
80
- }
81
-
82
- /**
83
- * To locate the root path containing the package.json file
84
- * @param path
85
- * @private
86
- */
87
- private async findLocalRoot(path: string): Promise<string | null> {
88
- try {
89
- const dir: string | undefined = await this.packageDirectory({cwd: path})
90
- return dir ? dir : null
91
- } catch (e) {
92
- return null
93
- }
94
- }
95
-
96
- /**
97
- * Convert string first char to upper case
98
- * @param str
99
- * @private
100
- */
101
- private stringFirstCharUpperCase(str: string): string {
102
- return str.charAt(0).toUpperCase() + str.slice(1)
103
- }
104
-
105
- /**
106
- * Format framework ascii logo
107
- * @protected
108
- */
109
- protected formatFrameworkAsciiLogo(): string {
110
- const asciiLogo: string = '' +
111
- ' _ _ _ \n' +
112
- '| | _ | | | | \n' +
113
- '| | __ _ | | _ _ _ | |_ __ _ | |_ __ _ \n' +
114
- '| | / _` | | |/ / | | | | | __| / _` | | __| / _` | \n' +
115
- '| |____ | (_| | | < | |_| | \\ |_ | (_| | \\ |_ | (_| | \n' +
116
- '|______| \\__,_| |_|\\_\\ \\__,_| \\__| \\__,_| \\__| \\__,_| \n' +
117
- ' '
118
- return `${ansis.yellow(asciiLogo)}`
119
- }
120
-
121
- /**
122
- * Format framework description
123
- * @protected
124
- */
125
- protected formatFrameworkDescription(): string {
126
- return `${ansis.bold(this.stringFirstCharUpperCase(this.name))} is ${ansis.blue(this.description)}`
127
- }
128
-
129
- /**
130
- * Format version text
131
- * @protected
132
- */
133
- protected formatVersionText(): string {
134
- return `The current version is ${ansis.bold(this.version)}`
135
- }
136
-
137
- /**
138
- * Format framework installation path
139
- * @protected
140
- */
141
- protected formatInstallationPath(): string {
142
- return `The installation directory path is ${ansis.underline(this.getInstallPath())}`
143
- }
144
-
145
- /**
146
- * Format current running level
147
- * @protected
148
- */
149
- protected formatRunningLevel(): string {
150
- return `Currently running at ${ansis.bold(this.getLevel())} level`
151
- }
152
-
153
- /**
154
- * Format license
155
- * @protected
156
- */
157
- protected formatLicense(): string {
158
- return `${this.stringFirstCharUpperCase(this.name)} is ${ansis.cyan(this.license)} licensed.`
159
- }
160
-
161
- /**
162
- * Format copyright
163
- * @protected
164
- */
165
- protected formatCopyright(): string {
166
- return `Copyright (c) ${new Time().format('YYYY')} ${ansis.bold(this.stringFirstCharUpperCase(this.name))}. All rights reserved.`
167
- }
168
-
169
- /**
170
- * To retrieve the hierarchy level of the project containing the package under the current command
171
- */
172
- public getLevel(): 'PROJECT' | 'GLOBAL' {
173
- return this.projectRoot ? 'PROJECT' : 'GLOBAL'
174
- }
175
-
176
- /**
177
- * Locate the root path of the project
178
- */
179
- public getRoot(): string | null {
180
- return this.projectRoot
181
- }
182
-
183
- /**
184
- * Locate the installation directory
185
- */
186
- public getInstallPath(): string {
187
- return this.installPath
188
- }
189
-
190
- /**
191
- * Retrieve the framework installation version at the project level
192
- */
193
- public async getProjectLevelInstalledFrameworkVersion(): Promise<string | null> {
194
- const projectRoot: string | null = this.getRoot()
195
- if (projectRoot) {
196
- try {
197
- const version: string = JSON.parse(await readFile(resolve(projectRoot, './node_modules', `./${this.name}/package.json`), {encoding: 'utf-8'})).version
198
- return version ? version : null
199
- } catch (e) {
200
- return null
201
- }
202
- }
203
- return null
204
- }
205
-
206
- /**
207
- * Print infos
208
- */
209
- public async print(): Promise<void> {
210
- const texts: string[] = [
211
- this.formatFrameworkAsciiLogo(),
212
- this.formatFrameworkDescription(),
213
- this.formatVersionText(),
214
- this.formatInstallationPath(),
215
- this.formatRunningLevel(),
216
- this.formatLicense(),
217
- this.formatCopyright()
218
- ]
219
- console.info(texts.join('\n'))
220
- }
221
- }
@@ -1,82 +0,0 @@
1
- import {ProjectTypeConfig} from '../lib/ProjectTypeConfig'
2
- import {DTO} from 'lakutata'
3
- import {Expect} from 'lakutata/decorator/dto'
4
-
5
- /**
6
- * Create project options
7
- */
8
- export class CreateProjectOptions extends DTO {
9
-
10
- @Expect(
11
- DTO
12
- .String()
13
- .required()
14
- .pattern(/^(\w+\.?)*\w+$/)
15
- .description('specify the name of the project and application')
16
- )
17
- public name: string
18
-
19
- @Expect(
20
- DTO
21
- .String()
22
- .required()
23
- .pattern(/^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/)//Match package json name regex
24
- .description('specify the ID of the application')
25
- )
26
- public id: string
27
-
28
- @Expect(
29
- DTO
30
- .String()
31
- .required()
32
- .valid(...Object.keys(ProjectTypeConfig))
33
- .description(`select the type of the project (choices: ${Object.keys(ProjectTypeConfig).map((type: string): string => `"${type}"`).join(',')})`)
34
- )
35
- public type: string
36
-
37
- @Expect(
38
- DTO
39
- .String()
40
- .optional()
41
- .default(process.cwd())
42
- .description(`specify the path for creating the project (default: "${process.cwd()}")`)
43
- )
44
- public path: string
45
-
46
- @Expect(
47
- DTO
48
- .Boolean()
49
- .strict(false)
50
- .optional()
51
- .default(false)
52
- .description('initialize project only in specified directory without creating a new folder (default: false)')
53
- )
54
- public initOnly: boolean
55
-
56
- @Expect(
57
- DTO
58
- .String()
59
- .optional()
60
- .default('none')
61
- .description('specify the description of the application (default: "none")')
62
- )
63
- public description: string
64
-
65
- @Expect(
66
- DTO
67
- .String()
68
- .optional()
69
- .default('Anonymous')
70
- .description('specify the name of the author of the application (default: "Anonymous")')
71
- )
72
- public author: string
73
-
74
- @Expect(
75
- DTO
76
- .String()
77
- .optional()
78
- .default('UNLICENSED')
79
- .description('specify the name of the license for the application (default: "UNLICENSED")')
80
- )
81
- public license: string
82
- }
@@ -1,4 +0,0 @@
1
- import {DTO} from 'lakutata'
2
-
3
- export class LakutataInfoOptions extends DTO {
4
- }