@i-santos/create-package-starter 1.0.0 → 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.
- package/README.md +18 -0
- package/lib/run.js +212 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,13 +7,22 @@ Scaffold new npm packages with a standardized Changesets release workflow.
|
|
|
7
7
|
```bash
|
|
8
8
|
npx @i-santos/create-package-starter --name hello-package
|
|
9
9
|
npx @i-santos/create-package-starter --name @i-santos/swarm
|
|
10
|
+
npx @i-santos/create-package-starter init --dir ./existing-package
|
|
10
11
|
```
|
|
11
12
|
|
|
12
13
|
## Options
|
|
13
14
|
|
|
15
|
+
Create new package:
|
|
16
|
+
|
|
14
17
|
- `--name <name>` (required, supports `pkg` and `@scope/pkg`)
|
|
15
18
|
- `--out <directory>` (default: current directory)
|
|
16
19
|
|
|
20
|
+
Bootstrap existing package:
|
|
21
|
+
|
|
22
|
+
- `init`
|
|
23
|
+
- `--dir <directory>` (default: current directory)
|
|
24
|
+
- `--force` (overwrite managed files/scripts/dependency keys)
|
|
25
|
+
|
|
17
26
|
## Output
|
|
18
27
|
|
|
19
28
|
Generated package includes:
|
|
@@ -26,6 +35,15 @@ Generated package includes:
|
|
|
26
35
|
|
|
27
36
|
plus a minimal README, CHANGELOG, `.gitignore`, and check script.
|
|
28
37
|
|
|
38
|
+
## Existing Project Bootstrap
|
|
39
|
+
|
|
40
|
+
`init` configures an existing npm package directory in-place:
|
|
41
|
+
|
|
42
|
+
- ensures scripts `changeset`, `version-packages`, `release`
|
|
43
|
+
- ensures `@changesets/cli` in `devDependencies`
|
|
44
|
+
- creates (or preserves) `.changeset/config.json`, `.changeset/README.md`, and `.github/workflows/release.yml`
|
|
45
|
+
- default mode is safe-merge; use `--force` to overwrite managed files/keys
|
|
46
|
+
|
|
29
47
|
## Notes
|
|
30
48
|
|
|
31
49
|
- For scoped names, folder uses the short package name.
|
package/lib/run.js
CHANGED
|
@@ -5,15 +5,17 @@ function usage() {
|
|
|
5
5
|
return [
|
|
6
6
|
'Uso:',
|
|
7
7
|
' create-package-starter --name <nome> [--out <diretorio>]',
|
|
8
|
+
' create-package-starter init [--dir <diretorio>] [--force]',
|
|
8
9
|
'',
|
|
9
10
|
'Exemplo:',
|
|
10
11
|
' create-package-starter --name hello-package',
|
|
11
|
-
' create-package-starter --name @i-santos/swarm',
|
|
12
|
-
' create-package-starter
|
|
12
|
+
' create-package-starter --name @i-santos/swarm --out ./packages',
|
|
13
|
+
' create-package-starter init --dir ./meu-pacote',
|
|
14
|
+
' create-package-starter init --force'
|
|
13
15
|
].join('\n');
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
function
|
|
18
|
+
function parseCreateArgs(argv) {
|
|
17
19
|
const args = {
|
|
18
20
|
out: process.cwd()
|
|
19
21
|
};
|
|
@@ -44,6 +46,51 @@ function parseArgs(argv) {
|
|
|
44
46
|
return args;
|
|
45
47
|
}
|
|
46
48
|
|
|
49
|
+
function parseInitArgs(argv) {
|
|
50
|
+
const args = {
|
|
51
|
+
dir: process.cwd(),
|
|
52
|
+
force: false
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
56
|
+
const token = argv[i];
|
|
57
|
+
|
|
58
|
+
if (token === '--dir') {
|
|
59
|
+
args.dir = argv[i + 1];
|
|
60
|
+
i += 1;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (token === '--force') {
|
|
65
|
+
args.force = true;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (token === '--help' || token === '-h') {
|
|
70
|
+
args.help = true;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
throw new Error(`Argumento inválido: ${token}\n\n${usage()}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return args;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function parseArgs(argv) {
|
|
81
|
+
if (argv[0] === 'init') {
|
|
82
|
+
return {
|
|
83
|
+
mode: 'init',
|
|
84
|
+
args: parseInitArgs(argv.slice(1))
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
mode: 'create',
|
|
90
|
+
args: parseCreateArgs(argv)
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
47
94
|
function validateName(name) {
|
|
48
95
|
if (typeof name !== 'string') {
|
|
49
96
|
return false;
|
|
@@ -83,14 +130,147 @@ function renderTemplateFile(filePath, variables) {
|
|
|
83
130
|
fs.writeFileSync(filePath, output);
|
|
84
131
|
}
|
|
85
132
|
|
|
86
|
-
|
|
87
|
-
|
|
133
|
+
function readJsonFile(filePath) {
|
|
134
|
+
let raw;
|
|
88
135
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
136
|
+
try {
|
|
137
|
+
raw = fs.readFileSync(filePath, 'utf8');
|
|
138
|
+
} catch (error) {
|
|
139
|
+
throw new Error(`Erro ao ler ${filePath}: ${error.message}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
return JSON.parse(raw);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
throw new Error(`Erro ao parsear JSON em ${filePath}: ${error.message}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function writeJsonFile(filePath, value) {
|
|
150
|
+
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function ensureFileFromTemplate(targetPath, templatePath, options) {
|
|
154
|
+
if (!fs.existsSync(templatePath)) {
|
|
155
|
+
throw new Error(`Erro: template não encontrado em ${templatePath}`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const exists = fs.existsSync(targetPath);
|
|
159
|
+
|
|
160
|
+
if (exists && !options.force) {
|
|
161
|
+
return 'skipped';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
165
|
+
fs.copyFileSync(templatePath, targetPath);
|
|
166
|
+
|
|
167
|
+
if (exists) {
|
|
168
|
+
return 'overwritten';
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return 'created';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function configureExistingPackage(packageDir, templateDir, force) {
|
|
175
|
+
if (!fs.existsSync(packageDir)) {
|
|
176
|
+
throw new Error(`Erro: diretório não encontrado: ${packageDir}`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const packageJsonPath = path.join(packageDir, 'package.json');
|
|
180
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
181
|
+
throw new Error(`Erro: package.json não encontrado em ${packageDir}.`);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const packageJson = readJsonFile(packageJsonPath);
|
|
185
|
+
packageJson.scripts = packageJson.scripts || {};
|
|
186
|
+
packageJson.devDependencies = packageJson.devDependencies || {};
|
|
187
|
+
|
|
188
|
+
const desiredScripts = {
|
|
189
|
+
changeset: 'changeset',
|
|
190
|
+
'version-packages': 'changeset version',
|
|
191
|
+
release: 'npm run check && changeset publish'
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const summary = {
|
|
195
|
+
createdFiles: [],
|
|
196
|
+
overwrittenFiles: [],
|
|
197
|
+
skippedFiles: [],
|
|
198
|
+
updatedScriptKeys: [],
|
|
199
|
+
skippedScriptKeys: [],
|
|
200
|
+
updatedDependencyKeys: [],
|
|
201
|
+
skippedDependencyKeys: []
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
let packageJsonChanged = false;
|
|
205
|
+
|
|
206
|
+
for (const [key, value] of Object.entries(desiredScripts)) {
|
|
207
|
+
const exists = Object.prototype.hasOwnProperty.call(packageJson.scripts, key);
|
|
208
|
+
|
|
209
|
+
if (!exists || force) {
|
|
210
|
+
if (!exists || packageJson.scripts[key] !== value) {
|
|
211
|
+
packageJson.scripts[key] = value;
|
|
212
|
+
packageJsonChanged = true;
|
|
213
|
+
}
|
|
214
|
+
summary.updatedScriptKeys.push(key);
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
summary.skippedScriptKeys.push(key);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const dependencyKey = '@changesets/cli';
|
|
222
|
+
const dependencyValue = '^2.29.7';
|
|
223
|
+
const depExists = Object.prototype.hasOwnProperty.call(packageJson.devDependencies, dependencyKey);
|
|
224
|
+
|
|
225
|
+
if (!depExists || force) {
|
|
226
|
+
if (!depExists || packageJson.devDependencies[dependencyKey] !== dependencyValue) {
|
|
227
|
+
packageJson.devDependencies[dependencyKey] = dependencyValue;
|
|
228
|
+
packageJsonChanged = true;
|
|
229
|
+
}
|
|
230
|
+
summary.updatedDependencyKeys.push(dependencyKey);
|
|
231
|
+
} else {
|
|
232
|
+
summary.skippedDependencyKeys.push(dependencyKey);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (packageJsonChanged) {
|
|
236
|
+
writeJsonFile(packageJsonPath, packageJson);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const fileSpecs = [
|
|
240
|
+
['.changeset/config.json', '.changeset/config.json'],
|
|
241
|
+
['.changeset/README.md', '.changeset/README.md'],
|
|
242
|
+
['.github/workflows/release.yml', '.github/workflows/release.yml']
|
|
243
|
+
];
|
|
244
|
+
|
|
245
|
+
for (const [targetRelativePath, templateRelativePath] of fileSpecs) {
|
|
246
|
+
const targetPath = path.join(packageDir, targetRelativePath);
|
|
247
|
+
const templatePath = path.join(templateDir, templateRelativePath);
|
|
248
|
+
const result = ensureFileFromTemplate(targetPath, templatePath, { force });
|
|
249
|
+
|
|
250
|
+
if (result === 'created') {
|
|
251
|
+
summary.createdFiles.push(targetRelativePath);
|
|
252
|
+
} else if (result === 'overwritten') {
|
|
253
|
+
summary.overwrittenFiles.push(targetRelativePath);
|
|
254
|
+
} else {
|
|
255
|
+
summary.skippedFiles.push(targetRelativePath);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (!packageJson.scripts.check) {
|
|
260
|
+
console.warn('Aviso: script "check" não encontrado. O script "release" executa "npm run check".');
|
|
92
261
|
}
|
|
93
262
|
|
|
263
|
+
console.log(`Projeto inicializado em ${packageDir}`);
|
|
264
|
+
console.log(`Arquivos criados: ${summary.createdFiles.length ? summary.createdFiles.join(', ') : 'nenhum'}`);
|
|
265
|
+
console.log(`Arquivos sobrescritos: ${summary.overwrittenFiles.length ? summary.overwrittenFiles.join(', ') : 'nenhum'}`);
|
|
266
|
+
console.log(`Arquivos ignorados: ${summary.skippedFiles.length ? summary.skippedFiles.join(', ') : 'nenhum'}`);
|
|
267
|
+
console.log(`Scripts atualizados: ${summary.updatedScriptKeys.length ? summary.updatedScriptKeys.join(', ') : 'nenhum'}`);
|
|
268
|
+
console.log(`Scripts preservados: ${summary.skippedScriptKeys.length ? summary.skippedScriptKeys.join(', ') : 'nenhum'}`);
|
|
269
|
+
console.log(`Dependências atualizadas: ${summary.updatedDependencyKeys.length ? summary.updatedDependencyKeys.join(', ') : 'nenhum'}`);
|
|
270
|
+
console.log(`Dependências preservadas: ${summary.skippedDependencyKeys.length ? summary.skippedDependencyKeys.join(', ') : 'nenhum'}`);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function createNewPackage(args) {
|
|
94
274
|
if (!validateName(args.name)) {
|
|
95
275
|
throw new Error('Erro: informe um nome válido com --name (ex: hello-package ou @i-santos/swarm).');
|
|
96
276
|
}
|
|
@@ -122,4 +302,28 @@ async function run(argv) {
|
|
|
122
302
|
console.log(`Pacote criado em ${targetDir}`);
|
|
123
303
|
}
|
|
124
304
|
|
|
305
|
+
function initExistingPackage(args) {
|
|
306
|
+
const packageRoot = path.resolve(__dirname, '..');
|
|
307
|
+
const templateDir = path.join(packageRoot, 'template');
|
|
308
|
+
const targetDir = path.resolve(args.dir);
|
|
309
|
+
|
|
310
|
+
configureExistingPackage(targetDir, templateDir, args.force);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async function run(argv) {
|
|
314
|
+
const parsed = parseArgs(argv);
|
|
315
|
+
|
|
316
|
+
if (parsed.args.help) {
|
|
317
|
+
console.log(usage());
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (parsed.mode === 'init') {
|
|
322
|
+
initExistingPackage(parsed.args);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
createNewPackage(parsed.args);
|
|
327
|
+
}
|
|
328
|
+
|
|
125
329
|
module.exports = { run };
|