progga 1.0.4 → 1.0.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.
- package/README.md +85 -71
- package/core/PresetSelector.js +23 -0
- package/core/ProjectTypeDetector.js +34 -0
- package/core/Spinner.js +32 -0
- package/index.js +76 -37
- package/package.json +7 -2
- package/profiles/FlutterProfile.js +65 -0
- package/profiles/ProfileRegistry.js +5 -0
package/README.md
CHANGED
|
@@ -1,110 +1,124 @@
|
|
|
1
1
|
# progga (প্রজ্ঞা)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Progga** is a CLI tool that generates a single Markdown file representing the essential context of a software project.
|
|
4
|
+
The output is optimized for uploading to AI assistants (ChatGPT, Claude, Gemini) so they can understand a project quickly and accurately.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
---
|
|
6
7
|
|
|
7
|
-
##
|
|
8
|
+
## Getting Started
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
### Run with npx (recommended)
|
|
10
11
|
|
|
11
|
-
- 💬 Get AI help with your entire codebase
|
|
12
|
-
- 📤 Share project context without multiple file uploads
|
|
13
|
-
- 🤖 Enable ChatGPT/Claude/Gemini to understand your project structure
|
|
14
|
-
- 📚 Create comprehensive documentation snapshots
|
|
15
|
-
|
|
16
|
-
## ✨ Features
|
|
17
|
-
|
|
18
|
-
- 📁 Visual folder tree structure
|
|
19
|
-
- 📄 All file contents with syntax highlighting
|
|
20
|
-
- 🚫 Automatically ignores dependencies and build artifacts
|
|
21
|
-
- ⚡ One command, one file, complete context
|
|
22
|
-
- 🎯 Optimized for AI consumption
|
|
23
|
-
|
|
24
|
-
## 🚀 Installation
|
|
25
|
-
|
|
26
|
-
### Using npx (Recommended - No Installation!)
|
|
27
12
|
```bash
|
|
28
13
|
npx progga@latest
|
|
29
14
|
```
|
|
30
15
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
16
|
+
This generates a file named:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
PROJECT_DOCUMENTATION.md
|
|
34
20
|
```
|
|
35
21
|
|
|
36
|
-
|
|
22
|
+
in the current directory.
|
|
37
23
|
|
|
38
|
-
###
|
|
24
|
+
### Run on a specific project
|
|
39
25
|
|
|
40
|
-
Generate documentation for current directory:
|
|
41
26
|
```bash
|
|
42
|
-
|
|
27
|
+
progga /path/to/project
|
|
43
28
|
```
|
|
44
29
|
|
|
45
|
-
|
|
30
|
+
### Custom output file
|
|
46
31
|
|
|
47
|
-
### Specify Project Path
|
|
48
32
|
```bash
|
|
49
|
-
|
|
33
|
+
progga . my-ai-context.md
|
|
50
34
|
```
|
|
51
35
|
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
npx progga@latest . my-ai-context.md
|
|
55
|
-
```
|
|
36
|
+
---
|
|
56
37
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
38
|
+
## How Progga Works (Short Example)
|
|
39
|
+
|
|
40
|
+
Given a project like:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
my-app/
|
|
44
|
+
├── src/
|
|
45
|
+
│ └── index.js
|
|
46
|
+
├── package.json
|
|
47
|
+
├── node_modules/
|
|
48
|
+
└── build/
|
|
60
49
|
```
|
|
61
50
|
|
|
62
|
-
|
|
51
|
+
Progga generates a single Markdown file containing:
|
|
63
52
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
53
|
+
* A clean folder tree (excluding `node_modules`, `build`, etc.)
|
|
54
|
+
* The contents of relevant source files
|
|
55
|
+
* Proper code blocks with language hints
|
|
67
56
|
|
|
68
|
-
Example
|
|
69
|
-
- "Review my code architecture"
|
|
70
|
-
- "Find potential bugs"
|
|
71
|
-
- "Suggest improvements"
|
|
72
|
-
- "Explain how this project works"
|
|
73
|
-
- "Help me add a new feature"
|
|
57
|
+
Example output structure:
|
|
74
58
|
|
|
75
|
-
|
|
59
|
+
````markdown
|
|
60
|
+
# Project Documentation: my-app
|
|
76
61
|
|
|
77
|
-
|
|
78
|
-
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
- Lock files
|
|
83
|
-
- Binary files (images, videos, fonts)
|
|
62
|
+
## Folder Structure
|
|
63
|
+
my-app/
|
|
64
|
+
├── src/
|
|
65
|
+
│ └── index.js
|
|
66
|
+
├── package.json
|
|
84
67
|
|
|
85
|
-
##
|
|
86
|
-
|
|
87
|
-
|
|
68
|
+
## File Contents
|
|
69
|
+
### src/index.js
|
|
70
|
+
```js
|
|
71
|
+
// file content here
|
|
72
|
+
````
|
|
88
73
|
|
|
89
|
-
|
|
90
|
-
[Visual tree of all files and folders]
|
|
74
|
+
You can upload this file directly to an AI and ask questions about the project.
|
|
91
75
|
|
|
92
|
-
##
|
|
93
|
-
|
|
94
|
-
|
|
76
|
+
## Project Presets
|
|
77
|
+
|
|
78
|
+
Progga supports project-type presets that control what files are included.
|
|
79
|
+
|
|
80
|
+
Currently supported:
|
|
81
|
+
- `generic` (default)
|
|
82
|
+
- `flutter` (Android, iOS, Web, Windows, macOS, Linux)
|
|
83
|
+
|
|
84
|
+
If no preset is provided, Progga attempts to detect the project type and asks which preset to use.
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
progga --preset flutter
|
|
88
|
+
````
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Contributing
|
|
93
|
+
|
|
94
|
+
Contributions are welcome.
|
|
95
|
+
|
|
96
|
+
Good areas to contribute:
|
|
97
|
+
|
|
98
|
+
* New project presets (Node.js, Python, Go, etc.)
|
|
99
|
+
* Improving Flutter include-only rules
|
|
100
|
+
* Performance improvements
|
|
101
|
+
* Better project auto-detection
|
|
102
|
+
* Documentation and examples
|
|
103
|
+
|
|
104
|
+
### How to contribute
|
|
105
|
+
|
|
106
|
+
1. Fork the repository
|
|
107
|
+
2. Create a feature branch
|
|
108
|
+
3. Make focused changes
|
|
109
|
+
4. Open a pull request with a clear description
|
|
95
110
|
|
|
96
|
-
|
|
111
|
+
Opening an issue to discuss ideas is also encouraged.
|
|
97
112
|
|
|
98
|
-
|
|
113
|
+
---
|
|
99
114
|
|
|
100
|
-
##
|
|
115
|
+
## Requirements
|
|
101
116
|
|
|
102
|
-
|
|
117
|
+
* Node.js 12 or newer (Node 18+ recommended)
|
|
103
118
|
|
|
104
|
-
|
|
119
|
+
---
|
|
105
120
|
|
|
106
|
-
|
|
121
|
+
## License
|
|
107
122
|
|
|
108
|
-
|
|
123
|
+
MIT License
|
|
109
124
|
|
|
110
|
-
**Progga** (প্রজ্ঞা) is a Bengali word meaning "wisdom" or "insight" - representing the wisdom you share with AI assistants about your codebase.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const select = require('@inquirer/select').default;
|
|
2
|
+
|
|
3
|
+
class PresetSelector {
|
|
4
|
+
static async choose(detectedType) {
|
|
5
|
+
const preset = await select({
|
|
6
|
+
message: 'Select how you want to proceed:',
|
|
7
|
+
choices: [
|
|
8
|
+
{
|
|
9
|
+
name: `Use ${detectedType} preset (recommended)`,
|
|
10
|
+
value: detectedType
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: 'Use generic preset',
|
|
14
|
+
value: 'generic'
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
return preset;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = PresetSelector;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
class ProjectTypeDetector {
|
|
5
|
+
constructor(projectRoot) {
|
|
6
|
+
this.projectRoot = projectRoot;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
detect() {
|
|
10
|
+
const signals = [];
|
|
11
|
+
|
|
12
|
+
if (this.isFlutterProject()) {
|
|
13
|
+
signals.push({
|
|
14
|
+
type: 'flutter',
|
|
15
|
+
confidence: 'high',
|
|
16
|
+
reason: 'pubspec.yaml found'
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// future:
|
|
21
|
+
// if (this.isNodeProject()) ...
|
|
22
|
+
// if (this.isPythonProject()) ...
|
|
23
|
+
|
|
24
|
+
return signals;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
isFlutterProject() {
|
|
28
|
+
return fs.existsSync(
|
|
29
|
+
path.join(this.projectRoot, 'pubspec.yaml')
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = ProjectTypeDetector;
|
package/core/Spinner.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const ora = require('ora').default;
|
|
2
|
+
|
|
3
|
+
class Spinner {
|
|
4
|
+
constructor(text) {
|
|
5
|
+
this.enabled = process.stdout.isTTY;
|
|
6
|
+
this.spinner = this.enabled ? ora(text).start() : null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
succeed(text) {
|
|
10
|
+
if (this.spinner) {
|
|
11
|
+
this.spinner.succeed(text);
|
|
12
|
+
} else {
|
|
13
|
+
console.log(`✓ ${text}`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
fail(text) {
|
|
18
|
+
if (this.spinner) {
|
|
19
|
+
this.spinner.fail(text);
|
|
20
|
+
} else {
|
|
21
|
+
console.error(`✗ ${text}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
update(text) {
|
|
26
|
+
if (this.spinner) {
|
|
27
|
+
this.spinner.text = text;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = Spinner;
|
package/index.js
CHANGED
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
const fs = require('fs');
|
|
9
9
|
const path = require('path');
|
|
10
10
|
const ProfileRegistry = require('./profiles/ProfileRegistry');
|
|
11
|
-
|
|
11
|
+
const ProjectTypeDetector = require('./core/ProjectTypeDetector');
|
|
12
|
+
const PresetSelector = require('./core/PresetSelector');
|
|
13
|
+
const Spinner = require('./core/Spinner');
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* Check if path should be ignored
|
|
@@ -50,43 +52,43 @@ function isDirectoryEmpty(directory, basePath) {
|
|
|
50
52
|
/**
|
|
51
53
|
* Generate tree structure recursively
|
|
52
54
|
*/
|
|
53
|
-
function generateTree(
|
|
55
|
+
function generateTree(directory, prefix = '', isLast = true, basePath = null, profile) {
|
|
54
56
|
if (basePath === null) {
|
|
55
57
|
basePath = directory;
|
|
56
58
|
}
|
|
57
|
-
|
|
59
|
+
|
|
58
60
|
const treeLines = [];
|
|
59
|
-
|
|
61
|
+
|
|
60
62
|
try {
|
|
61
63
|
let items = fs.readdirSync(directory).map(name => {
|
|
62
64
|
const fullPath = path.join(directory, name);
|
|
63
65
|
const stats = fs.statSync(fullPath);
|
|
64
66
|
return { name, fullPath, isDir: stats.isDirectory() };
|
|
65
67
|
});
|
|
66
|
-
|
|
68
|
+
|
|
67
69
|
// Filter ignored items
|
|
68
70
|
items = items.filter(item => !shouldIgnore(item.fullPath, basePath, profile));
|
|
69
|
-
|
|
71
|
+
|
|
70
72
|
// Sort: directories first, then alphabetically
|
|
71
73
|
items.sort((a, b) => {
|
|
72
74
|
if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;
|
|
73
75
|
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
|
|
74
76
|
});
|
|
75
|
-
|
|
77
|
+
|
|
76
78
|
for (let i = 0; i < items.length; i++) {
|
|
77
79
|
const item = items[i];
|
|
78
80
|
const isLastItem = i === items.length - 1;
|
|
79
|
-
|
|
81
|
+
|
|
80
82
|
// Tree characters
|
|
81
83
|
const connector = isLastItem ? '└── ' : '├── ';
|
|
82
84
|
const extension = isLastItem ? ' ' : '│ ';
|
|
83
|
-
|
|
85
|
+
|
|
84
86
|
if (item.isDir) {
|
|
85
87
|
if (isDirectoryEmpty(item.fullPath, basePath)) {
|
|
86
88
|
treeLines.push(`${prefix}${connector}${item.name}/ (empty)`);
|
|
87
89
|
} else {
|
|
88
90
|
treeLines.push(`${prefix}${connector}${item.name}/`);
|
|
89
|
-
const subtree = generateTree(
|
|
91
|
+
const subtree = generateTree(item.fullPath, prefix + extension, isLastItem, basePath, profile);
|
|
90
92
|
treeLines.push(...subtree);
|
|
91
93
|
}
|
|
92
94
|
} else {
|
|
@@ -102,7 +104,7 @@ function generateTree( directory, prefix = '', isLast = true, basePath = null, p
|
|
|
102
104
|
} catch (err) {
|
|
103
105
|
// Permission error or other issues
|
|
104
106
|
}
|
|
105
|
-
|
|
107
|
+
|
|
106
108
|
return treeLines;
|
|
107
109
|
}
|
|
108
110
|
|
|
@@ -140,7 +142,7 @@ function readFileContent(filePath) {
|
|
|
140
142
|
if (stats.size === 0) {
|
|
141
143
|
return '(empty file)';
|
|
142
144
|
}
|
|
143
|
-
|
|
145
|
+
|
|
144
146
|
return fs.readFileSync(filePath, 'utf-8');
|
|
145
147
|
} catch (err) {
|
|
146
148
|
if (err.message.includes('invalid')) {
|
|
@@ -182,7 +184,7 @@ function getLanguageFromExtension(filePath) {
|
|
|
182
184
|
'.rb': 'ruby',
|
|
183
185
|
'.php': 'php',
|
|
184
186
|
};
|
|
185
|
-
|
|
187
|
+
|
|
186
188
|
const ext = path.extname(filePath);
|
|
187
189
|
return extMap[ext] || '';
|
|
188
190
|
}
|
|
@@ -211,7 +213,7 @@ function collectFiles(directory, basePath, profile) {
|
|
|
211
213
|
files.push(...collectFiles(fullPath, basePath, profile));
|
|
212
214
|
}
|
|
213
215
|
}
|
|
214
|
-
} catch {}
|
|
216
|
+
} catch { }
|
|
215
217
|
|
|
216
218
|
return files;
|
|
217
219
|
}
|
|
@@ -221,15 +223,15 @@ function collectFiles(directory, basePath, profile) {
|
|
|
221
223
|
*/
|
|
222
224
|
function generateDocumentation(projectPath, outputFile, profile) {
|
|
223
225
|
const absProjectPath = path.resolve(projectPath);
|
|
224
|
-
|
|
226
|
+
|
|
225
227
|
if (!fs.existsSync(absProjectPath)) {
|
|
226
228
|
console.error(`Error: Path '${absProjectPath}' does not exist`);
|
|
227
229
|
process.exit(1);
|
|
228
230
|
}
|
|
229
|
-
|
|
231
|
+
|
|
230
232
|
console.log(`Generating documentation for: ${absProjectPath}`);
|
|
231
233
|
console.log(`Output file: ${outputFile}`);
|
|
232
|
-
|
|
234
|
+
|
|
233
235
|
// Delete existing output file if it exists
|
|
234
236
|
if (fs.existsSync(outputFile)) {
|
|
235
237
|
try {
|
|
@@ -239,43 +241,48 @@ function generateDocumentation(projectPath, outputFile, profile) {
|
|
|
239
241
|
console.warn(`Warning: Could not delete existing file: ${err.message}`);
|
|
240
242
|
}
|
|
241
243
|
}
|
|
242
|
-
|
|
244
|
+
|
|
243
245
|
const projectName = path.basename(absProjectPath);
|
|
244
246
|
let output = '';
|
|
245
|
-
|
|
247
|
+
|
|
246
248
|
// Write header
|
|
247
249
|
output += `# Project Documentation: ${projectName}\n\n`;
|
|
248
250
|
output += `**Generated from:** \`${absProjectPath}\`\n\n`;
|
|
249
251
|
output += '---\n\n';
|
|
250
|
-
|
|
252
|
+
|
|
251
253
|
// Write folder structure
|
|
252
254
|
output += '## 📁 Folder Structure\n\n';
|
|
253
255
|
output += '```\n';
|
|
254
256
|
output += `${projectName}/\n`;
|
|
255
|
-
|
|
257
|
+
|
|
258
|
+
const treeSpinner = new Spinner('Generating folder structure');
|
|
256
259
|
const treeLines = generateTree(absProjectPath, '', true, absProjectPath, profile);
|
|
260
|
+
treeSpinner.succeed('Folder structure generated');
|
|
261
|
+
|
|
257
262
|
for (const line of treeLines) {
|
|
258
263
|
output += `${line}\n`;
|
|
259
264
|
}
|
|
260
|
-
|
|
265
|
+
|
|
261
266
|
output += '```\n\n';
|
|
262
267
|
output += '---\n\n';
|
|
263
|
-
|
|
268
|
+
|
|
264
269
|
// Write file contents
|
|
265
270
|
output += '## 📄 File Contents\n\n';
|
|
266
|
-
|
|
271
|
+
|
|
272
|
+
const fileSpinner = new Spinner('Collecting files');
|
|
267
273
|
const files = collectFiles(absProjectPath, absProjectPath, profile);
|
|
268
|
-
|
|
274
|
+
fileSpinner.succeed(`Collected ${files.length} files`);
|
|
275
|
+
|
|
269
276
|
for (let i = 0; i < files.length; i++) {
|
|
270
277
|
const filePath = files[i];
|
|
271
278
|
const relPath = path.relative(absProjectPath, filePath);
|
|
272
279
|
console.log(`Processing (${i + 1}/${files.length}): ${relPath}`);
|
|
273
|
-
|
|
280
|
+
|
|
274
281
|
output += `### \`${relPath}\`\n\n`;
|
|
275
|
-
|
|
282
|
+
|
|
276
283
|
const content = readFileContent(filePath);
|
|
277
284
|
const language = getLanguageFromExtension(filePath);
|
|
278
|
-
|
|
285
|
+
|
|
279
286
|
output += `\`\`\`${language}\n`;
|
|
280
287
|
output += content;
|
|
281
288
|
if (!content.endsWith('\n')) {
|
|
@@ -284,26 +291,58 @@ function generateDocumentation(projectPath, outputFile, profile) {
|
|
|
284
291
|
output += '```\n\n';
|
|
285
292
|
output += '---\n\n';
|
|
286
293
|
}
|
|
287
|
-
|
|
294
|
+
|
|
288
295
|
// Write to file
|
|
289
296
|
fs.writeFileSync(outputFile, output, 'utf-8');
|
|
290
|
-
|
|
297
|
+
|
|
291
298
|
console.log(`\n✅ Documentation generated successfully: ${outputFile}`);
|
|
292
299
|
console.log(`📊 Total files processed: ${files.length}`);
|
|
293
300
|
}
|
|
294
301
|
|
|
295
302
|
// Main execution
|
|
296
|
-
function main() {
|
|
303
|
+
async function main() {
|
|
297
304
|
const args = process.argv.slice(2);
|
|
298
305
|
|
|
299
|
-
|
|
300
|
-
|
|
306
|
+
let projectPath = '.';
|
|
307
|
+
let outputFile = 'PROJECT_DOCUMENTATION.md';
|
|
308
|
+
let projectType = null;
|
|
309
|
+
|
|
310
|
+
for (let i = 0; i < args.length; i++) {
|
|
311
|
+
const arg = args[i];
|
|
312
|
+
|
|
313
|
+
if (arg === '--project-type' || arg === '--preset') {
|
|
314
|
+
projectType = args[i + 1];
|
|
315
|
+
i++;
|
|
316
|
+
} else if (!projectPath) {
|
|
317
|
+
projectPath = arg;
|
|
318
|
+
} else if (!outputFile) {
|
|
319
|
+
outputFile = arg;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
let profile = ProfileRegistry.getByName(projectType, projectPath);
|
|
324
|
+
|
|
325
|
+
if (!profile) {
|
|
326
|
+
const detector = new ProjectTypeDetector(projectPath);
|
|
327
|
+
const detections = detector.detect();
|
|
328
|
+
|
|
329
|
+
const flutterSignal = detections.find(d => d.type === 'flutter');
|
|
301
330
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
331
|
+
if (flutterSignal && process.stdin.isTTY) {
|
|
332
|
+
console.log('');
|
|
333
|
+
console.log(`Detected project type: ${flutterSignal.type}`);
|
|
334
|
+
console.log('');
|
|
335
|
+
|
|
336
|
+
const selectedPreset = await PresetSelector.choose(flutterSignal.type);
|
|
337
|
+
profile = ProfileRegistry.getByName(selectedPreset, projectPath);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (!profile) {
|
|
342
|
+
profile = ProfileRegistry.fallback(projectPath);
|
|
343
|
+
}
|
|
306
344
|
|
|
345
|
+
console.log(`Using profile: ${profile.name}`);
|
|
307
346
|
generateDocumentation(projectPath, outputFile, profile);
|
|
308
347
|
}
|
|
309
348
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "progga",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Generate comprehensive project documentation for AI assistants - Share your entire codebase context in one markdown file",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -37,5 +37,10 @@
|
|
|
37
37
|
"bugs": {
|
|
38
38
|
"url": "https://github.com/Yousuf-Basir/progga/issues"
|
|
39
39
|
},
|
|
40
|
-
"homepage": "https://github.com/Yousuf-Basir/progga#readme"
|
|
40
|
+
"homepage": "https://github.com/Yousuf-Basir/progga#readme",
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@inquirer/select": "^5.0.4",
|
|
43
|
+
"inquirer": "^13.2.0",
|
|
44
|
+
"ora": "^9.0.0"
|
|
45
|
+
}
|
|
41
46
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const ProjectProfile = require('./ProjectProfile');
|
|
2
|
+
|
|
3
|
+
class FlutterProfile extends ProjectProfile {
|
|
4
|
+
get name() {
|
|
5
|
+
return 'flutter';
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
ignorePaths() {
|
|
9
|
+
return [
|
|
10
|
+
// Dart / Flutter
|
|
11
|
+
'.dart_tool',
|
|
12
|
+
'build',
|
|
13
|
+
'.flutter-plugins',
|
|
14
|
+
'.flutter-plugins-dependencies',
|
|
15
|
+
|
|
16
|
+
// Android
|
|
17
|
+
'android/.gradle',
|
|
18
|
+
'android/build',
|
|
19
|
+
'android/app/build',
|
|
20
|
+
|
|
21
|
+
// iOS / macOS
|
|
22
|
+
'ios/Flutter/ephemeral',
|
|
23
|
+
'ios/Runner.xcodeproj',
|
|
24
|
+
'ios/Runner.xcworkspace',
|
|
25
|
+
'macos/Flutter/ephemeral',
|
|
26
|
+
'macos/Runner.xcodeproj',
|
|
27
|
+
|
|
28
|
+
// Web
|
|
29
|
+
'build/web',
|
|
30
|
+
|
|
31
|
+
// Windows
|
|
32
|
+
'windows/build',
|
|
33
|
+
|
|
34
|
+
// Linux
|
|
35
|
+
'linux/build',
|
|
36
|
+
|
|
37
|
+
// Common IDE noise
|
|
38
|
+
'.git',
|
|
39
|
+
'.idea',
|
|
40
|
+
'.vscode',
|
|
41
|
+
'.DS_Store',
|
|
42
|
+
];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
ignoreExtensions() {
|
|
46
|
+
return [
|
|
47
|
+
// Assets & binaries
|
|
48
|
+
'.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico',
|
|
49
|
+
'.mp4', '.mp3', '.wav',
|
|
50
|
+
'.ttf', '.otf', '.woff', '.woff2',
|
|
51
|
+
|
|
52
|
+
// Archives
|
|
53
|
+
'.zip', '.rar', '.tar', '.gz',
|
|
54
|
+
|
|
55
|
+
// Compiled outputs
|
|
56
|
+
'.exe', '.dll', '.so', '.dylib',
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
binaryExtensions() {
|
|
61
|
+
return this.ignoreExtensions();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = FlutterProfile;
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
const GenericProfile = require('./GenericProfile');
|
|
2
|
+
const FlutterProfile = require('./FlutterProfile');
|
|
2
3
|
|
|
3
4
|
class ProfileRegistry {
|
|
4
5
|
static getByName(name, projectRoot) {
|
|
5
6
|
if (!name) return null;
|
|
6
7
|
|
|
8
|
+
if (name === 'flutter') {
|
|
9
|
+
return new FlutterProfile(projectRoot);
|
|
10
|
+
}
|
|
11
|
+
|
|
7
12
|
if (name === 'generic') {
|
|
8
13
|
return new GenericProfile(projectRoot);
|
|
9
14
|
}
|