@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.
Files changed (3) hide show
  1. package/README.md +18 -0
  2. package/lib/run.js +212 -8
  3. 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 --name hello-package --out ./packages'
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 parseArgs(argv) {
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
- async function run(argv) {
87
- const args = parseArgs(argv);
133
+ function readJsonFile(filePath) {
134
+ let raw;
88
135
 
89
- if (args.help) {
90
- console.log(usage());
91
- return;
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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@i-santos/create-package-starter",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Scaffold new npm packages with a standardized Changesets release workflow",
5
5
  "license": "MIT",
6
6
  "author": "Igor Santos",