quackage 1.0.56 → 1.0.57
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/package.json
CHANGED
|
@@ -20,6 +20,7 @@ let _Pict = new libCLIProgram(
|
|
|
20
20
|
require('./commands/Quackage-Command-UpdatePackage.js'),
|
|
21
21
|
require('./commands/Quackage-Command-UpdatePackage-Luxury.js'),
|
|
22
22
|
require('./commands/Quackage-Command-Lint.js'),
|
|
23
|
+
require('./commands/Quackage-Command-UpdateNodeGitignore.js'),
|
|
23
24
|
|
|
24
25
|
// Mocha test execution
|
|
25
26
|
require('./commands/Quackage-Command-RunMochaTests.js'),
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
const libCommandLineCommand = require('pict-service-commandlineutility').ServiceCommandLineCommand;
|
|
2
|
+
const libFS = require('fs');
|
|
3
|
+
const libPath = require('path');
|
|
4
|
+
|
|
5
|
+
class QuackageCommandUpdateNodeGitignore extends libCommandLineCommand
|
|
6
|
+
{
|
|
7
|
+
constructor(pFable, pManifest, pServiceHash)
|
|
8
|
+
{
|
|
9
|
+
super(pFable, pManifest, pServiceHash);
|
|
10
|
+
|
|
11
|
+
this.options.CommandKeyword = 'update-node-gitignore';
|
|
12
|
+
this.options.Description = 'Check the .gitignore in the current folder against a prototype node module .gitignore and add any missing entries';
|
|
13
|
+
this.options.Aliases.push('update-gitignore');
|
|
14
|
+
this.options.Aliases.push('gitignore');
|
|
15
|
+
|
|
16
|
+
this.options.CommandOptions.push({ Name: '-d, --dry-run', Description: 'Show what would be added without modifying the file' });
|
|
17
|
+
|
|
18
|
+
this.addCommand();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Parse a .gitignore file into an array of structured blocks.
|
|
22
|
+
// Each block is { comment: string|null, entries: string[] }
|
|
23
|
+
// representing a comment header and the entries that follow it.
|
|
24
|
+
parseGitignore(pContent)
|
|
25
|
+
{
|
|
26
|
+
let tmpLines = pContent.split('\n');
|
|
27
|
+
let tmpBlocks = [];
|
|
28
|
+
let tmpCurrentBlock = { comment: null, entries: [] };
|
|
29
|
+
|
|
30
|
+
for (let i = 0; i < tmpLines.length; i++)
|
|
31
|
+
{
|
|
32
|
+
let tmpLine = tmpLines[i];
|
|
33
|
+
let tmpTrimmed = tmpLine.trim();
|
|
34
|
+
|
|
35
|
+
if (tmpTrimmed.startsWith('#'))
|
|
36
|
+
{
|
|
37
|
+
// If we have accumulated entries, push the current block
|
|
38
|
+
if (tmpCurrentBlock.comment !== null || tmpCurrentBlock.entries.length > 0)
|
|
39
|
+
{
|
|
40
|
+
tmpBlocks.push(tmpCurrentBlock);
|
|
41
|
+
}
|
|
42
|
+
tmpCurrentBlock = { comment: tmpLine, entries: [] };
|
|
43
|
+
}
|
|
44
|
+
else if (tmpTrimmed.length > 0)
|
|
45
|
+
{
|
|
46
|
+
tmpCurrentBlock.entries.push(tmpLine);
|
|
47
|
+
}
|
|
48
|
+
// blank lines are structural separators -- if we have a pending block with entries, close it
|
|
49
|
+
else if (tmpCurrentBlock.entries.length > 0)
|
|
50
|
+
{
|
|
51
|
+
tmpBlocks.push(tmpCurrentBlock);
|
|
52
|
+
tmpCurrentBlock = { comment: null, entries: [] };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Push any remaining block
|
|
57
|
+
if (tmpCurrentBlock.comment !== null || tmpCurrentBlock.entries.length > 0)
|
|
58
|
+
{
|
|
59
|
+
tmpBlocks.push(tmpCurrentBlock);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return tmpBlocks;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Collect all non-comment, non-blank entries from a gitignore into a Set
|
|
66
|
+
collectEntries(pContent)
|
|
67
|
+
{
|
|
68
|
+
let tmpEntries = new Set();
|
|
69
|
+
let tmpLines = pContent.split('\n');
|
|
70
|
+
for (let i = 0; i < tmpLines.length; i++)
|
|
71
|
+
{
|
|
72
|
+
let tmpTrimmed = tmpLines[i].trim();
|
|
73
|
+
if (tmpTrimmed.length > 0 && !tmpTrimmed.startsWith('#'))
|
|
74
|
+
{
|
|
75
|
+
tmpEntries.add(tmpTrimmed);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return tmpEntries;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
onRunAsync(fCallback)
|
|
82
|
+
{
|
|
83
|
+
let tmpOptions = this.CommandOptions;
|
|
84
|
+
let tmpCWD = this.fable.AppData.CWD;
|
|
85
|
+
let tmpProjectGitignorePath = `${tmpCWD}/.gitignore`;
|
|
86
|
+
|
|
87
|
+
// Load the prototype gitignore from quackage's own .gitignore
|
|
88
|
+
let tmpPrototypePath = libPath.join(this.fable.AppData.QuackageFolder, '.gitignore');
|
|
89
|
+
|
|
90
|
+
if (!libFS.existsSync(tmpPrototypePath))
|
|
91
|
+
{
|
|
92
|
+
this.log.error(`Prototype .gitignore not found at [${tmpPrototypePath}]`);
|
|
93
|
+
return fCallback(new Error('Prototype .gitignore not found'));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let tmpPrototypeContent = libFS.readFileSync(tmpPrototypePath, 'utf8');
|
|
97
|
+
|
|
98
|
+
// Load or create the project gitignore
|
|
99
|
+
let tmpProjectContent = '';
|
|
100
|
+
let tmpProjectExists = libFS.existsSync(tmpProjectGitignorePath);
|
|
101
|
+
if (tmpProjectExists)
|
|
102
|
+
{
|
|
103
|
+
tmpProjectContent = libFS.readFileSync(tmpProjectGitignorePath, 'utf8');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
this.log.info(`Checking .gitignore in [${tmpCWD}] against prototype...`);
|
|
107
|
+
|
|
108
|
+
// Collect all existing entries from the project gitignore
|
|
109
|
+
let tmpExistingEntries = this.collectEntries(tmpProjectContent);
|
|
110
|
+
|
|
111
|
+
// Parse the prototype into blocks so we can add missing entries with their section context
|
|
112
|
+
let tmpPrototypeBlocks = this.parseGitignore(tmpPrototypeContent);
|
|
113
|
+
|
|
114
|
+
// Walk each prototype block and find entries missing from the project
|
|
115
|
+
let tmpMissingBlocks = [];
|
|
116
|
+
let tmpTotalMissing = 0;
|
|
117
|
+
|
|
118
|
+
for (let i = 0; i < tmpPrototypeBlocks.length; i++)
|
|
119
|
+
{
|
|
120
|
+
let tmpBlock = tmpPrototypeBlocks[i];
|
|
121
|
+
let tmpMissingEntries = [];
|
|
122
|
+
|
|
123
|
+
for (let j = 0; j < tmpBlock.entries.length; j++)
|
|
124
|
+
{
|
|
125
|
+
let tmpEntry = tmpBlock.entries[j].trim();
|
|
126
|
+
if (!tmpExistingEntries.has(tmpEntry))
|
|
127
|
+
{
|
|
128
|
+
tmpMissingEntries.push(tmpBlock.entries[j]);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (tmpMissingEntries.length > 0)
|
|
133
|
+
{
|
|
134
|
+
tmpMissingBlocks.push({ comment: tmpBlock.comment, entries: tmpMissingEntries });
|
|
135
|
+
tmpTotalMissing += tmpMissingEntries.length;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (tmpTotalMissing === 0)
|
|
140
|
+
{
|
|
141
|
+
this.log.info(` --> .gitignore is up to date; nothing to add.`);
|
|
142
|
+
return fCallback();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Report what will be added
|
|
146
|
+
this.log.info(` --> Found ${tmpTotalMissing} missing ${tmpTotalMissing === 1 ? 'entry' : 'entries'}:`);
|
|
147
|
+
for (let i = 0; i < tmpMissingBlocks.length; i++)
|
|
148
|
+
{
|
|
149
|
+
let tmpBlock = tmpMissingBlocks[i];
|
|
150
|
+
if (tmpBlock.comment)
|
|
151
|
+
{
|
|
152
|
+
this.log.info(` ${tmpBlock.comment}`);
|
|
153
|
+
}
|
|
154
|
+
for (let j = 0; j < tmpBlock.entries.length; j++)
|
|
155
|
+
{
|
|
156
|
+
this.log.info(` + ${tmpBlock.entries[j]}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (tmpOptions.dry_run || tmpOptions.dryRun)
|
|
161
|
+
{
|
|
162
|
+
this.log.info(` --> Dry run; no changes written.`);
|
|
163
|
+
return fCallback();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Build the section to append
|
|
167
|
+
let tmpAppendLines = [];
|
|
168
|
+
tmpAppendLines.push('');
|
|
169
|
+
tmpAppendLines.push('# Added by quackage update-node-gitignore');
|
|
170
|
+
|
|
171
|
+
for (let i = 0; i < tmpMissingBlocks.length; i++)
|
|
172
|
+
{
|
|
173
|
+
let tmpBlock = tmpMissingBlocks[i];
|
|
174
|
+
tmpAppendLines.push('');
|
|
175
|
+
if (tmpBlock.comment)
|
|
176
|
+
{
|
|
177
|
+
tmpAppendLines.push(tmpBlock.comment);
|
|
178
|
+
}
|
|
179
|
+
for (let j = 0; j < tmpBlock.entries.length; j++)
|
|
180
|
+
{
|
|
181
|
+
tmpAppendLines.push(tmpBlock.entries[j]);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
let tmpAppendContent = tmpAppendLines.join('\n') + '\n';
|
|
186
|
+
|
|
187
|
+
// Ensure the existing file ends with a newline before appending
|
|
188
|
+
if (tmpProjectContent.length > 0 && !tmpProjectContent.endsWith('\n'))
|
|
189
|
+
{
|
|
190
|
+
tmpAppendContent = '\n' + tmpAppendContent;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
let tmpNewContent = tmpProjectContent + tmpAppendContent;
|
|
194
|
+
|
|
195
|
+
if (tmpProjectExists)
|
|
196
|
+
{
|
|
197
|
+
this.log.info(` --> Backing up .gitignore to .gitignore.quackage.bak ...`);
|
|
198
|
+
libFS.writeFileSync(`${tmpCWD}/.gitignore.quackage.bak`, tmpProjectContent);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
this.log.info(` --> Writing updated .gitignore ...`);
|
|
202
|
+
libFS.writeFileSync(tmpProjectGitignorePath, tmpNewContent);
|
|
203
|
+
this.log.info(` --> Done; ${tmpTotalMissing} ${tmpTotalMissing === 1 ? 'entry' : 'entries'} added.`);
|
|
204
|
+
|
|
205
|
+
return fCallback();
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
module.exports = QuackageCommandUpdateNodeGitignore;
|