create-tampermonkey-typescript 1.0.9 → 1.1.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.
Files changed (2) hide show
  1. package/dist/cli.js +285 -0
  2. package/package.json +1 -1
package/dist/cli.js ADDED
@@ -0,0 +1,285 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import prompts from 'prompts';
5
+ import kleur from 'kleur';
6
+ import { execSync } from 'node:child_process';
7
+ import { fileURLToPath } from 'url';
8
+ const __FILENAME = fileURLToPath(import.meta.url);
9
+ const __DIRNAME = path.dirname(__FILENAME);
10
+ const TEMPLATES_DIR = path.join(__DIRNAME, '../templates');
11
+ const defaultDevDeps = [
12
+ 'typescript', //
13
+ 'vite',
14
+ 'vite-plugin-banner',
15
+ 'vite-plugin-css-injected-by-js',
16
+ 'ts-node',
17
+ '@types/tampermonkey',
18
+ ];
19
+ const defaultDeps = [];
20
+ const baseTsConfig = {
21
+ compilerOptions: {
22
+ types: ['vite/client', 'node'],
23
+ target: 'ES2022',
24
+ module: 'ESNext',
25
+ moduleResolution: 'NodeNext',
26
+ strict: true,
27
+ esModuleInterop: true,
28
+ forceConsistentCasingInFileNames: true,
29
+ skipLibCheck: true,
30
+ resolveJsonModule: true,
31
+ outDir: 'dist',
32
+ baseUrl: '.',
33
+ paths: {
34
+ '@/*': ['src/*'],
35
+ },
36
+ lib: ['ES2022', 'dom', 'dom.iterable'],
37
+ },
38
+ include: ['src'],
39
+ };
40
+ const availableFeatures = [
41
+ {
42
+ name: 'React',
43
+ description: 'Adds react support to the project',
44
+ directory: path.join(TEMPLATES_DIR, 'react'),
45
+ tsConfigModifier: (config) => (config.compilerOptions.jsx = 'react-jsx'),
46
+ devDependencies: ['react', 'react-dom', '@types/react', '@types/react-dom'],
47
+ },
48
+ {
49
+ name: 'Git',
50
+ description: 'Initializes the new project as a git repository',
51
+ directory: path.join(TEMPLATES_DIR, 'git'),
52
+ hook: (params) => {
53
+ fs.renameSync(path.join(params.projectPath, '_gitignore'), path.join(params.projectPath, '.gitignore'));
54
+ execInProjectDir(`git init && git add . && git commit -m "Initial commit"`, params);
55
+ },
56
+ },
57
+ {
58
+ name: 'Github Workflows',
59
+ description: 'Includes 2 workflows; one for tagging releases with current version, and another for building & creating a release',
60
+ directory: path.join(TEMPLATES_DIR, 'github-workflows'),
61
+ },
62
+ ];
63
+ const availablePackageManagers = [
64
+ {
65
+ name: 'npm',
66
+ exists: () => commandExists('npm --version'),
67
+ installCmd: 'npm install',
68
+ addDependencyCmd: 'npm install',
69
+ runCmd: 'npm run',
70
+ },
71
+ {
72
+ name: 'yarn',
73
+ exists: () => commandExists('yarn --version'),
74
+ installCmd: 'yarn install',
75
+ addDependencyCmd: 'yarn add',
76
+ runCmd: 'yarn run',
77
+ },
78
+ {
79
+ name: 'pnpm',
80
+ exists: () => commandExists('pnpm --version'),
81
+ installCmd: 'pnpm install',
82
+ addDependencyCmd: 'pnpm add',
83
+ runCmd: 'pnpm run',
84
+ },
85
+ ];
86
+ async function main() {
87
+ console.log(kleur.green('create-tampermonkey-typescript'));
88
+ if (!checkCwdAccess())
89
+ return;
90
+ if (!findValidPackageManager())
91
+ return;
92
+ // Prompt for project parameters
93
+ const promptResults = await promptForParams();
94
+ const params = {
95
+ ...promptResults,
96
+ projectPath: path.join(process.cwd(), promptResults.projectName),
97
+ features: promptResults.featureNames.map((featureName) => availableFeatures.find((a) => a.name === featureName)),
98
+ packageManager: availablePackageManagers.find((pm) => pm.name === promptResults.packageManagerName),
99
+ };
100
+ // Create the directory
101
+ logWithPrefix(`Creating directory ${kleur.yellow(params.projectPath)}`);
102
+ const createDirResult = await createProjectDirectory(params.projectPath);
103
+ if (typeof createDirResult === 'string') {
104
+ console.log(kleur.red('An occurred while attempting to create the project directory:'));
105
+ console.log(kleur.red(createDirResult));
106
+ return;
107
+ }
108
+ // Initialize package.json
109
+ logWithPrefix(`Creating ${kleur.yellow('package.json')}`);
110
+ const packageJson = createPackageJson(params);
111
+ writeObjectToFile(packageJson, 'package.json', params);
112
+ // Initialize the lock file
113
+ logWithPrefix(`Initializing lock file`);
114
+ execInProjectDir(`${params.packageManager.installCmd}`, params);
115
+ // Gather dependencies
116
+ const devDeps = [...defaultDevDeps, ...params.features.flatMap((f) => f.devDependencies ?? [])];
117
+ const deps = [...defaultDeps, ...params.features.flatMap((f) => f.dependencies ?? [])];
118
+ // Install dev dependencies
119
+ if (devDeps.length > 0) {
120
+ logWithPrefix('Installing dev dependencies');
121
+ console.log(` ${kleur.dim(devDeps.join(', '))}`);
122
+ execInProjectDir(`${params.packageManager.addDependencyCmd} -D ${devDeps.join(' ')}`, params);
123
+ }
124
+ // Install dependencies
125
+ if (deps.length > 0) {
126
+ logWithPrefix('Installing dependencies');
127
+ console.log(` ${kleur.dim(deps.join(', '))}`);
128
+ execInProjectDir(`${params.packageManager.addDependencyCmd} ${deps.join(' ')}`, params);
129
+ }
130
+ // Handle tsconfig.json
131
+ const tsConfig = { ...baseTsConfig };
132
+ for (const feature of params.features) {
133
+ if (feature.tsConfigModifier) {
134
+ feature.tsConfigModifier(tsConfig);
135
+ }
136
+ }
137
+ logWithPrefix(`Creating ${kleur.yellow('tsconfig.json')}`);
138
+ writeObjectToFile(tsConfig, 'tsconfig.json', params);
139
+ // Copy files
140
+ logWithPrefix('Copying project files');
141
+ // Base files
142
+ fs.cpSync(path.join(TEMPLATES_DIR, 'base'), params.projectPath, { recursive: true, force: true });
143
+ // README.md replacements
144
+ const readmePath = path.join(params.projectPath, 'README.md');
145
+ let content = fs.readFileSync(readmePath, 'utf-8');
146
+ content = content.replaceAll('{{PROJECT_NAME}}', params.projectName);
147
+ content = content.replaceAll('{{PROJECT_PATH}}', params.projectPath);
148
+ content = content.replaceAll('{{PM_ADD_DEPENDENCY}}', params.packageManager.addDependencyCmd);
149
+ content = content.replaceAll('{{PM_RUN_SCRIPT}}', params.packageManager.runCmd);
150
+ fs.writeFileSync(readmePath, content);
151
+ // Feature files
152
+ params.features
153
+ .filter((f) => f.directory)
154
+ .forEach((f) => fs.cpSync(f.directory, params.projectPath, { recursive: true, force: true }));
155
+ // Handle feature hooks
156
+ params.features
157
+ .filter((f) => f.hook)
158
+ .forEach((f) => {
159
+ logWithPrefix(`Running feature hook: ${kleur.yellow(f.name)}`);
160
+ f.hook(params);
161
+ });
162
+ console.log(`${kleur.green('Done!')} Project created at ${kleur.yellow(params.projectPath)}`);
163
+ }
164
+ function logWithPrefix(message) {
165
+ console.log(`${kleur.cyan('-')} ${kleur.white(message)}`);
166
+ }
167
+ function commandExists(cmd) {
168
+ try {
169
+ execSync(cmd, { stdio: 'ignore' });
170
+ return true;
171
+ }
172
+ catch {
173
+ return false;
174
+ }
175
+ }
176
+ function checkCwdAccess() {
177
+ try {
178
+ fs.accessSync(process.cwd(), fs.constants.W_OK | fs.constants.R_OK);
179
+ }
180
+ catch {
181
+ console.log(kleur.red('Read & write access to current working directory is required.'));
182
+ return false;
183
+ }
184
+ return true;
185
+ }
186
+ function findValidPackageManager() {
187
+ if (!availablePackageManagers.find((pm) => pm.exists())) {
188
+ console.log(kleur.red('Could not find any valid package manager: '));
189
+ console.log(kleur.yellow(`(${availablePackageManagers.map((pm) => pm.name).join(', ')})`));
190
+ return false;
191
+ }
192
+ return true;
193
+ }
194
+ function validateProjectName(projectName) {
195
+ if (!projectName || projectName.length === 0)
196
+ return 'Project name cannot be empty';
197
+ if (projectName.includes(' '))
198
+ return 'Project name cannot contain spaces';
199
+ const projectPath = path.join(process.cwd(), projectName);
200
+ try {
201
+ const stats = fs.statSync(projectPath);
202
+ if (!stats.isDirectory()) {
203
+ return `Path ${projectPath} already exists and is not a directory.`;
204
+ }
205
+ const files = fs.readdirSync(projectPath);
206
+ if (files.length > 0)
207
+ return `Directory ${projectPath} already exists and is not empty.`;
208
+ }
209
+ catch { }
210
+ return true;
211
+ }
212
+ async function promptForParams() {
213
+ return await prompts([
214
+ {
215
+ type: 'text',
216
+ name: 'projectName',
217
+ message: 'Project name:',
218
+ format: (s) => s.trim(),
219
+ validate: (s) => validateProjectName(s.trim()),
220
+ },
221
+ {
222
+ type: 'text',
223
+ name: 'version',
224
+ message: 'Version:',
225
+ },
226
+ {
227
+ type: 'text',
228
+ name: 'description',
229
+ message: 'Description:',
230
+ },
231
+ {
232
+ type: 'text',
233
+ name: 'author',
234
+ message: 'Author:',
235
+ },
236
+ {
237
+ type: 'select',
238
+ name: 'packageManagerName',
239
+ message: 'Package manager:',
240
+ choices: availablePackageManagers.filter((pm) => pm.exists()).map((pm) => ({ title: pm.name, value: pm.name })),
241
+ },
242
+ {
243
+ type: 'multiselect',
244
+ name: 'featureNames',
245
+ message: 'Select features:',
246
+ choices: availableFeatures.map((f) => ({ title: f.name, value: f.name, description: f.description })),
247
+ },
248
+ ], {
249
+ onCancel: () => process.exit(0),
250
+ });
251
+ }
252
+ async function createProjectDirectory(path) {
253
+ try {
254
+ fs.mkdirSync(path, { recursive: true });
255
+ return true;
256
+ }
257
+ catch (error) {
258
+ if (error instanceof Error) {
259
+ return error.message;
260
+ }
261
+ return 'Unknown error.';
262
+ }
263
+ }
264
+ function createPackageJson(params) {
265
+ return {
266
+ name: params.projectName,
267
+ description: params.description,
268
+ version: params.version,
269
+ author: params.author,
270
+ main: 'dist/script.user.js',
271
+ type: 'module',
272
+ scripts: {
273
+ build: 'vite build',
274
+ },
275
+ };
276
+ }
277
+ function writeObjectToFile(object, fileName, params) {
278
+ const filePath = path.join(params.projectPath, fileName);
279
+ const jsonString = JSON.stringify(object, null, 2);
280
+ fs.writeFileSync(filePath, jsonString);
281
+ }
282
+ function execInProjectDir(command, params) {
283
+ execSync(command, { stdio: 'inherit', cwd: params.projectPath });
284
+ }
285
+ main().then(() => { }, (e) => console.error(kleur.red(e.message || e)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-tampermonkey-typescript",
3
- "version": "1.0.9",
3
+ "version": "1.1.0",
4
4
  "description": "CLI tool for generating a TamperMonkey script project written in typescript which is transpiled to JavaScript to be ran in the browser",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/neth392/create-tampermonkey-typescript",