@i-santos/create-package-starter 0.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 ADDED
@@ -0,0 +1,34 @@
1
+ # @i-santos/create-package-starter
2
+
3
+ Scaffold new npm packages with a consistent release workflow.
4
+
5
+ ## Install / Run
6
+
7
+ ```bash
8
+ npx @i-santos/create-package-starter --name hello-package
9
+ npx @i-santos/create-package-starter --name @i-santos/swarm
10
+ ```
11
+
12
+ ## Options
13
+
14
+ - `--name <name>` (required, supports `pkg` and `@scope/pkg`)
15
+ - `--out <directory>` (default: current directory)
16
+ - `--release-cli-pkg <package>` (default: `@i-santos/release-cli`)
17
+ - `--release-cli-version <version>` (default: `^0.1.0`)
18
+
19
+ ## Output
20
+
21
+ Generated package includes:
22
+
23
+ - `release:beta`
24
+ - `release:stable`
25
+ - `release:publish`
26
+ - `registry:start`
27
+
28
+ plus a minimal README, CHANGELOG, `.gitignore`, and check script.
29
+
30
+ ## Notes
31
+
32
+ - For scoped names, folder uses the short package name.
33
+ - Example: `@i-santos/swarm` creates `./swarm`.
34
+ - Template follows `npm init -y` behavior by default (no `private` field).
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { run } = require('../lib/run');
4
+
5
+ run(process.argv.slice(2)).catch((error) => {
6
+ console.error(error.message);
7
+ process.exit(1);
8
+ });
package/lib/run.js ADDED
@@ -0,0 +1,154 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const DEFAULT_RELEASE_CLI_PKG = '@i-santos/release-cli';
5
+ const DEFAULT_RELEASE_CLI_VERSION = '^0.1.0';
6
+
7
+ function usage() {
8
+ return [
9
+ 'Uso:',
10
+ ' create-package-starter --name <nome> [--out <diretorio>] [--release-cli-pkg <pkg>] [--release-cli-version <versao>]',
11
+ '',
12
+ 'Exemplo:',
13
+ ' create-package-starter --name hello-package',
14
+ ' create-package-starter --name @i-santos/swarm',
15
+ ' create-package-starter --name hello-package --out ./packages',
16
+ ' create-package-starter --name hello-package --release-cli-pkg @i-santos/release-cli --release-cli-version ^1.0.0'
17
+ ].join('\n');
18
+ }
19
+
20
+ function parseArgs(argv) {
21
+ const args = {
22
+ out: process.cwd(),
23
+ releaseCliPkg: DEFAULT_RELEASE_CLI_PKG,
24
+ releaseCliVersion: DEFAULT_RELEASE_CLI_VERSION
25
+ };
26
+
27
+ for (let i = 0; i < argv.length; i += 1) {
28
+ const token = argv[i];
29
+
30
+ if (token === '--name') {
31
+ args.name = argv[i + 1];
32
+ i += 1;
33
+ continue;
34
+ }
35
+
36
+ if (token === '--out') {
37
+ args.out = argv[i + 1];
38
+ i += 1;
39
+ continue;
40
+ }
41
+
42
+ if (token === '--release-cli-pkg') {
43
+ args.releaseCliPkg = argv[i + 1];
44
+ i += 1;
45
+ continue;
46
+ }
47
+
48
+ if (token === '--release-cli-version') {
49
+ args.releaseCliVersion = argv[i + 1];
50
+ i += 1;
51
+ continue;
52
+ }
53
+
54
+ if (token === '--help' || token === '-h') {
55
+ args.help = true;
56
+ continue;
57
+ }
58
+
59
+ throw new Error(`Argumento inválido: ${token}\n\n${usage()}`);
60
+ }
61
+
62
+ return args;
63
+ }
64
+
65
+ function validateName(name) {
66
+ if (typeof name !== 'string') {
67
+ return false;
68
+ }
69
+
70
+ const plain = /^[a-z0-9][a-z0-9._-]*$/;
71
+ const scoped = /^@[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*$/;
72
+ return plain.test(name) || scoped.test(name);
73
+ }
74
+
75
+ function packageDirFromName(packageName) {
76
+ const parts = packageName.split('/');
77
+ return parts[parts.length - 1];
78
+ }
79
+
80
+ function copyDirRecursive(sourceDir, targetDir) {
81
+ fs.mkdirSync(targetDir, { recursive: true });
82
+ const entries = fs.readdirSync(sourceDir, { withFileTypes: true });
83
+
84
+ for (const entry of entries) {
85
+ const srcPath = path.join(sourceDir, entry.name);
86
+ const destPath = path.join(targetDir, entry.name);
87
+
88
+ if (entry.isDirectory()) {
89
+ copyDirRecursive(srcPath, destPath);
90
+ continue;
91
+ }
92
+
93
+ fs.copyFileSync(srcPath, destPath);
94
+ }
95
+ }
96
+
97
+ function renderTemplateFile(filePath, variables) {
98
+ const source = fs.readFileSync(filePath, 'utf8');
99
+ const output = source
100
+ .replace(/__PACKAGE_NAME__/g, variables.packageName)
101
+ .replace(/__RELEASE_CLI_PKG__/g, variables.releaseCliPkg)
102
+ .replace(/__RELEASE_CLI_VERSION__/g, variables.releaseCliVersion);
103
+
104
+ fs.writeFileSync(filePath, output);
105
+ }
106
+
107
+ async function run(argv) {
108
+ const args = parseArgs(argv);
109
+
110
+ if (args.help) {
111
+ console.log(usage());
112
+ return;
113
+ }
114
+
115
+ if (!validateName(args.name)) {
116
+ throw new Error('Erro: informe um nome válido com --name (ex: hello-package ou @i-santos/swarm).');
117
+ }
118
+
119
+ if (!args.releaseCliPkg || !args.releaseCliVersion) {
120
+ throw new Error('Erro: --release-cli-pkg e --release-cli-version devem ser informados corretamente.');
121
+ }
122
+
123
+ const packageRoot = path.resolve(__dirname, '..');
124
+ const templateDir = path.join(packageRoot, 'template');
125
+
126
+ if (!fs.existsSync(templateDir)) {
127
+ throw new Error(`Erro: template não encontrado em ${templateDir}`);
128
+ }
129
+
130
+ const outputDir = path.resolve(args.out);
131
+ const targetDir = path.join(outputDir, packageDirFromName(args.name));
132
+
133
+ if (fs.existsSync(targetDir)) {
134
+ throw new Error(`Erro: diretório já existe: ${targetDir}`);
135
+ }
136
+
137
+ copyDirRecursive(templateDir, targetDir);
138
+
139
+ renderTemplateFile(path.join(targetDir, 'package.json'), {
140
+ packageName: args.name,
141
+ releaseCliPkg: args.releaseCliPkg,
142
+ releaseCliVersion: args.releaseCliVersion
143
+ });
144
+
145
+ renderTemplateFile(path.join(targetDir, 'README.md'), {
146
+ packageName: args.name,
147
+ releaseCliPkg: args.releaseCliPkg,
148
+ releaseCliVersion: args.releaseCliVersion
149
+ });
150
+
151
+ console.log(`Pacote criado em ${targetDir}`);
152
+ }
153
+
154
+ module.exports = { run };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@i-santos/create-package-starter",
3
+ "version": "0.1.0",
4
+ "description": "Scaffold new npm packages with standardized release scripts",
5
+ "license": "MIT",
6
+ "author": "Igor Santos",
7
+ "homepage": "https://github.com/i-santos/package-starter#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/i-santos/package-starter.git",
11
+ "directory": "packages/create-package-starter"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/i-santos/package-starter/issues"
15
+ },
16
+ "keywords": [
17
+ "scaffold",
18
+ "npm",
19
+ "starter",
20
+ "cli",
21
+ "template"
22
+ ],
23
+ "engines": {
24
+ "node": ">=18"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "bin": {
30
+ "create-package-starter": "bin/create-package-starter.js"
31
+ },
32
+ "files": [
33
+ "bin",
34
+ "lib",
35
+ "template",
36
+ "README.md"
37
+ ],
38
+ "scripts": {
39
+ "check": "node scripts/check.js"
40
+ }
41
+ }
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Estrutura inicial do pacote.
@@ -0,0 +1,15 @@
1
+ # __PACKAGE_NAME__
2
+
3
+ Pacote criado pelo `@i-santos/create-package-starter`.
4
+
5
+ ## Comandos
6
+
7
+ - `npm run check`
8
+ - `npm run registry:start`
9
+ - `npm run release:beta`
10
+ - `npm run release:stable`
11
+ - `npm run release:publish`
12
+
13
+ ## Dependência de release
14
+
15
+ Este pacote foi gerado para usar `__RELEASE_CLI_PKG__@__RELEASE_CLI_VERSION__`.
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "__PACKAGE_NAME__",
3
+ "version": "0.1.0",
4
+ "description": "Pacote gerado pelo create-package-starter",
5
+ "scripts": {
6
+ "check": "node scripts/check.js",
7
+ "release:beta": "release-cli beta",
8
+ "release:stable": "release-cli stable",
9
+ "release:publish": "release-cli publish",
10
+ "registry:start": "release-cli registry http://127.0.0.1:4873"
11
+ },
12
+ "devDependencies": {
13
+ "__RELEASE_CLI_PKG__": "__RELEASE_CLI_VERSION__"
14
+ }
15
+ }
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ const packageJsonPath = path.join(process.cwd(), 'package.json');
7
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
8
+
9
+ const requiredScripts = ['release:beta', 'release:stable', 'release:publish', 'registry:start'];
10
+ for (const scriptName of requiredScripts) {
11
+ if (!pkg.scripts || !pkg.scripts[scriptName]) {
12
+ console.error(`Script obrigatório ausente: ${scriptName}`);
13
+ process.exit(1);
14
+ }
15
+ }
16
+
17
+ console.log(`check ok para ${pkg.name}@${pkg.version}`);