boilerforge 1.0.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.
@@ -0,0 +1,306 @@
1
+ import { sep } from 'path';
2
+ import { File } from '../../lib/file';
3
+ import {
4
+ NodejsProject,
5
+ NodejsProjectProps,
6
+ ProjectDependency,
7
+ Resource,
8
+ } from '../../lib/project';
9
+ import { Prompter } from '../../lib/prompter';
10
+ import { Task } from '../../lib/task';
11
+ import { execCmd } from '../../utils/exec-cmd';
12
+ import { getGitConfiguration, getGitVersion } from '../utils/git';
13
+ import {
14
+ getLockFileName,
15
+ getPackageManagerVersion,
16
+ installDependencies,
17
+ } from '../utils/package-manager';
18
+ import { getReadmeContent } from '../utils/readme';
19
+ import { getEditorConfigContent } from './resource/editor-config';
20
+ import { getEslintContent } from './resource/eslint';
21
+ import { getGitIgnoreContent } from './resource/gitignore';
22
+ import { getPackageJsonContent } from './resource/package-json';
23
+ import {
24
+ getPrettierIgnoreContent,
25
+ getPrettierrcContent,
26
+ } from './resource/prettier';
27
+ import { getProjectDeps } from './resource/project-deps';
28
+ import { srcFileContent } from './resource/project-files';
29
+ import { getTsConfigContent } from './resource/tsconfig';
30
+ import { Regex } from '../../utils';
31
+
32
+ export class CreateNodeApp extends NodejsProject {
33
+ constructor(props: NodejsProjectProps) {
34
+ super(props);
35
+ this.isTypescript = false;
36
+ this.isEslintPrettier = false;
37
+ this.configureCliCommand();
38
+ }
39
+
40
+ async setup(): Promise<CreateNodeApp> {
41
+ // creating the tasks
42
+ const {
43
+ systemCheckTask,
44
+ setupProjectTask,
45
+ installDepsTask,
46
+ gitInitTask,
47
+ } = this.buildTasks(getProjectDeps(this));
48
+
49
+ this.tasks = [
50
+ systemCheckTask,
51
+ setupProjectTask,
52
+ installDepsTask,
53
+ gitInitTask,
54
+ ];
55
+
56
+ await this.bootstrap();
57
+
58
+ return this;
59
+ }
60
+
61
+ buildTasks(projectDeps: ProjectDependency): {
62
+ systemCheckTask: Task;
63
+ setupProjectTask: Task;
64
+ installDepsTask: Task;
65
+ gitInitTask: Task;
66
+ } {
67
+ // verify git
68
+ const verifyGitTask = new Task({
69
+ title: 'Checking Git availability',
70
+ task: async () => {
71
+ this.systemCheck.git = {
72
+ version: await getGitVersion(),
73
+ };
74
+ },
75
+ });
76
+
77
+ // git configuration (user.name & user.email)
78
+ const verifyGitConfigTask = new Task({
79
+ title: 'Checking Git config (user.name & user.email)',
80
+ enabled: () => !!this.systemCheck.git,
81
+ task: async () => {
82
+ const gitConfig = await getGitConfiguration({
83
+ global: true,
84
+ });
85
+
86
+ if (!gitConfig) return;
87
+
88
+ const username = gitConfig['user.name'];
89
+ const email = gitConfig['user.email'];
90
+
91
+ username &&
92
+ email &&
93
+ (this.systemCheck.git = {
94
+ ...this.systemCheck.git,
95
+ configuration: {
96
+ username,
97
+ email,
98
+ },
99
+ });
100
+ },
101
+ });
102
+
103
+ // verify package manager
104
+ const verifyPkgManagerTask = new Task({
105
+ title: `Verifying ${this.packageManager} availability`,
106
+ task: async () => {
107
+ const pkgManagerVersion = await getPackageManagerVersion(
108
+ this.packageManager,
109
+ );
110
+
111
+ if (!pkgManagerVersion)
112
+ throw new Error(`${this.packageManager} is not installed`);
113
+
114
+ this.packageManagerVersion = pkgManagerVersion;
115
+ this.systemCheck.packageManager = `${this.packageManager}@${pkgManagerVersion}`;
116
+ },
117
+ });
118
+
119
+ // checking system requirements
120
+ const systemCheckTask = new Task({
121
+ title: 'Verifying System Requirements',
122
+ subTasks: [
123
+ verifyGitTask,
124
+ verifyGitConfigTask,
125
+ verifyPkgManagerTask,
126
+ ],
127
+ });
128
+
129
+ // creating the project dir
130
+ const setupProjectTask = new Task({
131
+ title: `Scaffolding ${this.name} project`,
132
+ task: async () => {
133
+ const resourceFileContent: Resource[] = [
134
+ getGitIgnoreContent(),
135
+ getEditorConfigContent(),
136
+ ...(this.enabledEslintPrettier
137
+ ? [getEslintContent(this), getPrettierrcContent()]
138
+ : []),
139
+ getPackageJsonContent(this),
140
+ getPrettierIgnoreContent(),
141
+ ...(this.enabledTypescript ? [getTsConfigContent()] : []),
142
+ getReadmeContent(this),
143
+ srcFileContent(this),
144
+ ];
145
+
146
+ this.files = resourceFileContent.map(
147
+ ({ filename, content }) =>
148
+ new File(filename, {
149
+ dirPath: this.projectPath,
150
+ content: `${content.trim()}\n`,
151
+ }),
152
+ );
153
+
154
+ await Promise.all(this.files.map((file) => file.create()));
155
+ },
156
+ rollback: () => {
157
+ this.files.map((file) => file.delete());
158
+ },
159
+ });
160
+
161
+ // installing dependencies
162
+ const installDepsTask = new Task({
163
+ title: `Installing dependencies`,
164
+ task: async () => {
165
+ await installDependencies(
166
+ this.projectPath,
167
+ this.packageManager,
168
+ projectDeps,
169
+ );
170
+ },
171
+ rollback: async () => {
172
+ await execCmd(`rm -rf node_modules`, {
173
+ cwd: this.projectPath,
174
+ throwError: false,
175
+ });
176
+
177
+ const lockFile = getLockFileName(this.packageManager);
178
+
179
+ lockFile &&
180
+ (await execCmd(`rm ${lockFile}`, {
181
+ cwd: this.projectPath,
182
+ throwError: false,
183
+ }));
184
+ },
185
+ });
186
+
187
+ // initialize git
188
+ const gitInitTask = new Task({
189
+ title: 'Initializing git',
190
+ enabled: () => !!this.systemCheck.git?.version,
191
+ task: async () => {
192
+ await execCmd('git init', {
193
+ cwd: this.projectPath,
194
+ });
195
+
196
+ const { username, email } =
197
+ this.systemCheck.git?.configuration ?? {};
198
+
199
+ username &&
200
+ email &&
201
+ (await execCmd('git commit -am"✨ Initial project setup"', {
202
+ cwd: this.projectPath,
203
+ }));
204
+ },
205
+ rollback: async () =>
206
+ this.systemCheck.git?.version &&
207
+ (await execCmd(`rm -rf .git`, {
208
+ cwd: this.projectPath,
209
+ throwError: false,
210
+ })),
211
+ });
212
+
213
+ return {
214
+ systemCheckTask,
215
+ setupProjectTask,
216
+ installDepsTask,
217
+ gitInitTask,
218
+ };
219
+ }
220
+
221
+ async configureCliCommand(): Promise<CreateNodeApp> {
222
+ this.program
223
+ .command('create-node-app')
224
+ .description('Create simple node.js app')
225
+ .argument('[name]', 'name of the project', '')
226
+ .action(async (name) => {
227
+ this.name = name;
228
+ await this.promptUser();
229
+ await this.setup();
230
+ });
231
+
232
+ return this;
233
+ }
234
+
235
+ async promptUser(): Promise<void> {
236
+ const prompter = new Prompter([
237
+ {
238
+ type: 'input',
239
+ name: 'name',
240
+ default: '',
241
+ description: {
242
+ message: 'Project name',
243
+ default: process.cwd().split(sep).slice(-1)[0],
244
+ },
245
+ validate: (value) =>
246
+ !value.length || Regex.PROJECT_NAME.test(value)
247
+ ? true
248
+ : 'Invalid project name',
249
+ skip: !!this.name.length,
250
+ },
251
+ {
252
+ type: 'input',
253
+ name: 'description',
254
+ default: '',
255
+ description: { message: 'Description' },
256
+ },
257
+ {
258
+ type: 'input',
259
+ name: 'version',
260
+ default: '0.0.1',
261
+ description: { message: 'Version' },
262
+ validate: (value) =>
263
+ Regex.VERSION.test(value) ? true : 'Invalid version',
264
+ },
265
+ {
266
+ type: 'input',
267
+ name: 'author',
268
+ default: '',
269
+ description: { message: 'Author' },
270
+ },
271
+ {
272
+ type: 'select',
273
+ name: 'packageManager',
274
+ choices: ['npm', 'yarn', 'pnpm'],
275
+ default: 0,
276
+ description: { message: 'Package manager', default: 'npm' },
277
+ },
278
+ {
279
+ type: 'radio',
280
+ name: 'isTypescript',
281
+ enabled: 'Yes',
282
+ disabled: 'No',
283
+ default: true,
284
+ description: { message: 'Use TypeScript' },
285
+ },
286
+ {
287
+ type: 'radio',
288
+ name: 'isEslintPrettier',
289
+ enabled: 'Yes',
290
+ disabled: 'No',
291
+ default: true,
292
+ description: { message: 'Use ESLint & Prettier formatting' },
293
+ },
294
+ ] as const);
295
+
296
+ const { isEslintPrettier, isTypescript, ...restResponse } =
297
+ await prompter.prompt();
298
+
299
+ this.configureProject({
300
+ ...restResponse,
301
+ });
302
+
303
+ this.isTypescript = isTypescript;
304
+ this.isEslintPrettier = isEslintPrettier;
305
+ }
306
+ }
@@ -0,0 +1,24 @@
1
+ import { Resource } from '../../../lib/project';
2
+
3
+ const content = `
4
+ root = true
5
+
6
+ [*]
7
+ indent_style = space
8
+ indent_size = 4
9
+ end_of_line = lf
10
+ charset = utf-8
11
+ trim_trailing_whitespace = true
12
+ insert_final_newline = true
13
+
14
+ [*.yml]
15
+ indent_style = space
16
+ indent_size = 2
17
+ `;
18
+
19
+ export const getEditorConfigContent = (): Resource => {
20
+ return {
21
+ filename: '.editorconfig',
22
+ content,
23
+ };
24
+ };
@@ -0,0 +1,62 @@
1
+ import { NodejsProject, Resource } from '../../../lib/project';
2
+
3
+ const eslintTsContent = `
4
+ import js from "@eslint/js";
5
+ import globals from "globals";
6
+ import tseslint from "typescript-eslint";
7
+ import eslintPluginTs from '@typescript-eslint/eslint-plugin';
8
+ import { defineConfig } from "eslint/config";
9
+
10
+ export default defineConfig([
11
+ { files: ["**/*.{js,mjs,cjs,ts}"], plugins: { js }, extends: ["js/recommended"] },
12
+ { files: ["**/*.{js,mjs,cjs,ts}"], languageOptions: { globals: globals.node } },
13
+ tseslint.configs.recommended,
14
+ {
15
+ plugins: {
16
+ '@typescript-eslint': eslintPluginTs,
17
+ },
18
+ rules: {
19
+ "@typescript-eslint/no-namespace": "off",
20
+ "@typescript-eslint/no-empty-interface": "off",
21
+ "@typescript-eslint/no-empty-object-type": "off",
22
+ "@typescript-eslint/no-unused-expressions": "off"
23
+ }
24
+ },
25
+ { ignores: ["node_modules/*", "dist/*"] },
26
+ ]);
27
+ `;
28
+
29
+ const eslintJsContent = `
30
+ import js from "@eslint/js";
31
+ import globals from "globals";
32
+ import tseslint from "typescript-eslint";
33
+ import eslintPluginTs from '@typescript-eslint/eslint-plugin';
34
+ import { defineConfig } from "eslint/config";
35
+
36
+ export default defineConfig([
37
+ { files: ["**/*.{js,mjs,cjs,ts}"], plugins: { js }, extends: ["js/recommended"] },
38
+ { files: ["**/*.{js,mjs,cjs,ts}"], languageOptions: { globals: globals.node } },
39
+ tseslint.configs.recommended,
40
+ {
41
+ plugins: {
42
+ '@typescript-eslint': eslintPluginTs,
43
+ },
44
+ rules: {
45
+ "@typescript-eslint/no-namespace": "off",
46
+ "@typescript-eslint/no-empty-interface": "off",
47
+ "@typescript-eslint/no-empty-object-type": "off",
48
+ "@typescript-eslint/no-unused-expressions": "off"
49
+ }
50
+ },
51
+ { ignores: ["node_modules/*", "dist/*"] },
52
+ ]);
53
+ `;
54
+
55
+ export const getEslintContent = (project: NodejsProject): Resource => {
56
+ const filename = 'eslint.config.mjs';
57
+
58
+ return {
59
+ filename,
60
+ content: project.enabledTypescript ? eslintTsContent : eslintJsContent,
61
+ };
62
+ };
@@ -0,0 +1,155 @@
1
+ import { Resource } from '../../../lib/project';
2
+
3
+ const gitignore = `
4
+ # Logs
5
+ logs
6
+ *.log
7
+ npm-debug.log*
8
+ yarn-debug.log*
9
+ yarn-error.log*
10
+ lerna-debug.log*
11
+ .pnpm-debug.log*
12
+
13
+ # Diagnostic reports (https://nodejs.org/api/report.html)
14
+ report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
15
+
16
+ # Runtime data
17
+ pids
18
+ *.pid
19
+ *.seed
20
+ *.pid.lock
21
+
22
+ # Directory for instrumented libs generated by jscoverage/JSCover
23
+ lib-cov
24
+
25
+ # Coverage directory used by tools like istanbul
26
+ coverage
27
+ *.lcov
28
+
29
+ # nyc test coverage
30
+ .nyc_output
31
+
32
+ # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
33
+ .grunt
34
+
35
+ # Bower dependency directory (https://bower.io/)
36
+ bower_components
37
+
38
+ # node-waf configuration
39
+ .lock-wscript
40
+
41
+ # Compiled binary addons (https://nodejs.org/api/addons.html)
42
+ build/Release
43
+
44
+ # Dependency directories
45
+ node_modules/
46
+ jspm_packages/
47
+
48
+ # Snowpack dependency directory (https://snowpack.dev/)
49
+ web_modules/
50
+
51
+ # TypeScript cache
52
+ *.tsbuildinfo
53
+
54
+ # Optional npm cache directory
55
+ .npm
56
+
57
+ # Optional eslint cache
58
+ .eslintcache
59
+
60
+ # Optional stylelint cache
61
+ .stylelintcache
62
+
63
+ # Microbundle cache
64
+ .rpt2_cache/
65
+ .rts2_cache_cjs/
66
+ .rts2_cache_es/
67
+ .rts2_cache_umd/
68
+
69
+ # Optional REPL history
70
+ .node_repl_history
71
+
72
+ # Output of 'npm pack'
73
+ *.tgz
74
+
75
+ # Yarn Integrity file
76
+ .yarn-integrity
77
+
78
+ # dotenv environment variable files
79
+ .env
80
+ .env.development.local
81
+ .env.test.local
82
+ .env.production.local
83
+ .env.local
84
+
85
+ # parcel-bundler cache (https://parceljs.org/)
86
+ .cache
87
+ .parcel-cache
88
+
89
+ # Next.js build output
90
+ .next
91
+ out
92
+
93
+ # Nuxt.js build / generate output
94
+ .nuxt
95
+ dist
96
+
97
+ # Gatsby files
98
+ .cache/
99
+ # Comment in the public line in if your project uses Gatsby and not Next.js
100
+ # https://nextjs.org/blog/next-9-1#public-directory-support
101
+ # public
102
+
103
+ # vuepress build output
104
+ .vuepress/dist
105
+
106
+ # vuepress v2.x temp and cache directory
107
+ .temp
108
+ .cache
109
+
110
+ # vitepress build output
111
+ **/.vitepress/dist
112
+
113
+ # vitepress cache directory
114
+ **/.vitepress/cache
115
+
116
+ # Docusaurus cache and generated files
117
+ .docusaurus
118
+
119
+ # Serverless directories
120
+ .serverless/
121
+
122
+ # FuseBox cache
123
+ .fusebox/
124
+
125
+ # DynamoDB Local files
126
+ .dynamodb/
127
+
128
+ # TernJS port file
129
+ .tern-port
130
+
131
+ # Stores VSCode versions used for testing VSCode extensions
132
+ .vscode-test
133
+
134
+ # yarn v2
135
+ .yarn/cache
136
+ .yarn/unplugged
137
+ .yarn/build-state.yml
138
+ .yarn/install-state.gz
139
+ .pnp.*
140
+
141
+ # build or bundles
142
+ build/**/*
143
+
144
+ # Ignore built ts files
145
+ dist/**/*
146
+ `;
147
+
148
+ export const getGitIgnoreContent = (): Resource => {
149
+ const filename = '.gitignore';
150
+
151
+ return {
152
+ filename,
153
+ content: gitignore,
154
+ };
155
+ };
@@ -0,0 +1,57 @@
1
+ import { NodejsProject, Resource } from '../../../lib/project';
2
+ import { PackageManager } from '../../utils/package-manager';
3
+
4
+ export const getPackageJsonContent = (project: NodejsProject): Resource => {
5
+ const { name, author, description, packageManager, version } = project;
6
+
7
+ const ci = `${packageManager} ${packageManager === PackageManager.NPM ? 'ci' : 'install --frozen-lockfile'}`;
8
+
9
+ const prebuildScript = [`${packageManager} run clean`];
10
+
11
+ project.enabledEslintPrettier &&
12
+ prebuildScript.push(
13
+ `${packageManager} run lint`,
14
+ `${packageManager} run format`,
15
+ );
16
+
17
+ const packageJson = {
18
+ name,
19
+ version,
20
+ description,
21
+ author,
22
+ license: 'ISC',
23
+ packageManager: project.getPackageManager(),
24
+ main: project.enabledTypescript ? 'dist/' : 'src/',
25
+ scripts: {
26
+ ci,
27
+ ...(project.enabledTypescript
28
+ ? {
29
+ clean: 'rimraf dist',
30
+ build: 'tsc',
31
+ dev: 'ts-node src/',
32
+ prebuild: prebuildScript.join(' && '),
33
+ start: 'node dist/',
34
+ ...(project.enabledEslintPrettier
35
+ ? {
36
+ lint: 'eslint --ext .js,.ts .',
37
+ format: 'prettier --write "**/*.+(js|ts)"',
38
+ }
39
+ : {}),
40
+ }
41
+ : {
42
+ start: 'node src/',
43
+ ...(project.enabledEslintPrettier
44
+ ? {
45
+ lint: 'eslint --ext .js .',
46
+ format: 'prettier --write "**/*.+(js)"',
47
+ }
48
+ : {}),
49
+ }),
50
+ },
51
+ };
52
+
53
+ return {
54
+ filename: 'package.json',
55
+ content: JSON.stringify(packageJson, null, 4),
56
+ };
57
+ };
@@ -0,0 +1,45 @@
1
+ import { Resource } from '../../../lib/project';
2
+
3
+ const prettierignore = `
4
+ .gitignore
5
+ .editorconfig
6
+ *.json
7
+
8
+ node_modules/
9
+
10
+ # build or bundles
11
+ build/
12
+
13
+ # Ignore built js files
14
+ dist/
15
+ `;
16
+
17
+ export const getPrettierrcContent = (): Resource => {
18
+ const prettierrcJson = {
19
+ printWidth: 80,
20
+ tabWidth: 4,
21
+ useTabs: false,
22
+ semi: true,
23
+ singleQuote: true,
24
+ quoteProps: 'as-needed',
25
+ trailingComma: 'all',
26
+ arrowParens: 'always',
27
+ parser: 'typescript',
28
+ proseWrap: 'always',
29
+ endOfLine: 'auto',
30
+ embeddedLanguageFormatting: 'auto',
31
+ singleAttributePerLine: true,
32
+ };
33
+
34
+ return {
35
+ filename: '.prettierrc.json',
36
+ content: JSON.stringify(prettierrcJson, null, 4),
37
+ };
38
+ };
39
+
40
+ export const getPrettierIgnoreContent = (): Resource => {
41
+ return {
42
+ filename: '.prettierignore',
43
+ content: prettierignore,
44
+ };
45
+ };
@@ -0,0 +1,30 @@
1
+ import { NodejsProject, ProjectDependency } from '../../../lib/project';
2
+
3
+ export const getProjectDeps = (project: NodejsProject): ProjectDependency => {
4
+ const { enabledEslintPrettier, enabledTypescript } = project;
5
+
6
+ const esLintPrettierDeps = enabledEslintPrettier
7
+ ? [
8
+ '@eslint/js',
9
+ 'eslint',
10
+ 'globals',
11
+ 'prettier',
12
+ ...(enabledTypescript
13
+ ? [
14
+ '@typescript-eslint/eslint-plugin',
15
+ '@typescript-eslint/parser',
16
+ 'typescript-eslint',
17
+ ]
18
+ : []),
19
+ ]
20
+ : [];
21
+
22
+ const tsDeps = enabledTypescript
23
+ ? ['@types/node', 'ts-node', 'typescript', 'rimraf']
24
+ : [];
25
+
26
+ return {
27
+ dependencies: [],
28
+ devDependencies: [...esLintPrettierDeps, ...tsDeps],
29
+ };
30
+ };
@@ -0,0 +1,17 @@
1
+ import { NodejsProject, Resource } from '../../../lib/project';
2
+
3
+ export const srcFileContent = (project: NodejsProject): Resource => {
4
+ const filename = `src/index.${project.enabledTypescript ? 'ts' : 'js'}`;
5
+ const content = `
6
+ async function main() {
7
+ console.log('Hello from ${project.name}');
8
+ }
9
+
10
+ main().catch((error) => console.error(error));
11
+ `;
12
+
13
+ return {
14
+ filename,
15
+ content,
16
+ };
17
+ };