@i-santos/create-package-starter 1.3.0 → 1.4.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 +20 -0
- package/lib/run.js +143 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@ npx @i-santos/create-package-starter --name hello-package
|
|
|
9
9
|
npx @i-santos/create-package-starter --name @i-santos/swarm --default-branch main
|
|
10
10
|
npx @i-santos/create-package-starter init --dir ./existing-package
|
|
11
11
|
npx @i-santos/create-package-starter setup-github --repo i-santos/firestack --dry-run
|
|
12
|
+
npx @i-santos/create-package-starter setup-npm --dir ./existing-package --publish-first
|
|
12
13
|
```
|
|
13
14
|
|
|
14
15
|
## Commands
|
|
@@ -36,6 +37,13 @@ Configure GitHub repository settings:
|
|
|
36
37
|
- `--ruleset <path>` (optional JSON override)
|
|
37
38
|
- `--dry-run` (prints intended operations only)
|
|
38
39
|
|
|
40
|
+
Bootstrap npm publishing:
|
|
41
|
+
|
|
42
|
+
- `setup-npm`
|
|
43
|
+
- `--dir <directory>` (default: current directory)
|
|
44
|
+
- `--publish-first` (run `npm publish --access public` only when package is not found on npm)
|
|
45
|
+
- `--dry-run` (prints intended operations only)
|
|
46
|
+
|
|
39
47
|
## Managed Standards
|
|
40
48
|
|
|
41
49
|
The generated and managed baseline includes:
|
|
@@ -84,6 +92,18 @@ All commands print a deterministic summary with:
|
|
|
84
92
|
|
|
85
93
|
If `gh` is missing or unauthenticated, command exits non-zero with actionable guidance.
|
|
86
94
|
|
|
95
|
+
## setup-npm Behavior
|
|
96
|
+
|
|
97
|
+
`setup-npm` validates npm publish readiness:
|
|
98
|
+
|
|
99
|
+
- checks npm CLI availability
|
|
100
|
+
- checks npm authentication (`npm whoami`)
|
|
101
|
+
- checks whether package already exists on npm
|
|
102
|
+
- optionally performs first publish (`--publish-first`)
|
|
103
|
+
- prints next steps for Trusted Publisher configuration
|
|
104
|
+
|
|
105
|
+
Important: Trusted Publisher still needs manual setup in npm package settings.
|
|
106
|
+
|
|
87
107
|
## Trusted Publishing Note
|
|
88
108
|
|
|
89
109
|
If package does not exist on npm yet, first publish may be manual:
|
package/lib/run.js
CHANGED
|
@@ -25,13 +25,15 @@ function usage() {
|
|
|
25
25
|
' create-package-starter --name <name> [--out <directory>] [--default-branch <branch>]',
|
|
26
26
|
' create-package-starter init [--dir <directory>] [--force] [--cleanup-legacy-release] [--scope <scope>] [--default-branch <branch>]',
|
|
27
27
|
' create-package-starter setup-github [--repo <owner/repo>] [--default-branch <branch>] [--ruleset <path>] [--dry-run]',
|
|
28
|
+
' create-package-starter setup-npm [--dir <directory>] [--publish-first] [--dry-run]',
|
|
28
29
|
'',
|
|
29
30
|
'Examples:',
|
|
30
31
|
' create-package-starter --name hello-package',
|
|
31
32
|
' create-package-starter --name @i-santos/swarm --out ./packages',
|
|
32
33
|
' create-package-starter init --dir ./my-package',
|
|
33
34
|
' create-package-starter init --cleanup-legacy-release',
|
|
34
|
-
' create-package-starter setup-github --repo i-santos/firestack --dry-run'
|
|
35
|
+
' create-package-starter setup-github --repo i-santos/firestack --dry-run',
|
|
36
|
+
' create-package-starter setup-npm --dir . --publish-first'
|
|
35
37
|
].join('\n');
|
|
36
38
|
}
|
|
37
39
|
|
|
@@ -176,6 +178,43 @@ function parseSetupGithubArgs(argv) {
|
|
|
176
178
|
return args;
|
|
177
179
|
}
|
|
178
180
|
|
|
181
|
+
function parseSetupNpmArgs(argv) {
|
|
182
|
+
const args = {
|
|
183
|
+
dir: process.cwd(),
|
|
184
|
+
publishFirst: false,
|
|
185
|
+
dryRun: false
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
189
|
+
const token = argv[i];
|
|
190
|
+
|
|
191
|
+
if (token === '--dir') {
|
|
192
|
+
args.dir = parseValueFlag(argv, i, '--dir');
|
|
193
|
+
i += 1;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (token === '--publish-first') {
|
|
198
|
+
args.publishFirst = true;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (token === '--dry-run') {
|
|
203
|
+
args.dryRun = true;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (token === '--help' || token === '-h') {
|
|
208
|
+
args.help = true;
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
throw new Error(`Invalid argument: ${token}\n\n${usage()}`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return args;
|
|
216
|
+
}
|
|
217
|
+
|
|
179
218
|
function parseArgs(argv) {
|
|
180
219
|
if (argv[0] === 'init') {
|
|
181
220
|
return {
|
|
@@ -191,6 +230,13 @@ function parseArgs(argv) {
|
|
|
191
230
|
};
|
|
192
231
|
}
|
|
193
232
|
|
|
233
|
+
if (argv[0] === 'setup-npm') {
|
|
234
|
+
return {
|
|
235
|
+
mode: 'setup-npm',
|
|
236
|
+
args: parseSetupNpmArgs(argv.slice(1))
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
194
240
|
return {
|
|
195
241
|
mode: 'create',
|
|
196
242
|
args: parseCreateArgs(argv)
|
|
@@ -683,6 +729,95 @@ function updateWorkflowPermissions(deps, repo) {
|
|
|
683
729
|
}
|
|
684
730
|
}
|
|
685
731
|
|
|
732
|
+
function ensureNpmAvailable(deps) {
|
|
733
|
+
const version = deps.exec('npm', ['--version']);
|
|
734
|
+
if (version.status !== 0) {
|
|
735
|
+
throw new Error('npm CLI is required. Install npm and rerun.');
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
function ensureNpmAuthenticated(deps) {
|
|
740
|
+
const whoami = deps.exec('npm', ['whoami']);
|
|
741
|
+
if (whoami.status !== 0) {
|
|
742
|
+
throw new Error('npm CLI is not authenticated. Run "npm login" and rerun.');
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
function packageExistsOnNpm(deps, packageName) {
|
|
747
|
+
const view = deps.exec('npm', ['view', packageName, 'version', '--json']);
|
|
748
|
+
if (view.status === 0) {
|
|
749
|
+
return true;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
const output = `${view.stderr || ''}\n${view.stdout || ''}`.toLowerCase();
|
|
753
|
+
if (output.includes('e404') || output.includes('not found') || output.includes('404')) {
|
|
754
|
+
return false;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
throw new Error(`Failed to check package on npm: ${view.stderr || view.stdout}`.trim());
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function setupNpm(args, dependencies = {}) {
|
|
761
|
+
const deps = {
|
|
762
|
+
exec: dependencies.exec || execCommand
|
|
763
|
+
};
|
|
764
|
+
|
|
765
|
+
const targetDir = path.resolve(args.dir);
|
|
766
|
+
if (!fs.existsSync(targetDir)) {
|
|
767
|
+
throw new Error(`Directory not found: ${targetDir}`);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
const packageJsonPath = path.join(targetDir, 'package.json');
|
|
771
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
772
|
+
throw new Error(`package.json not found in ${targetDir}`);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
const packageJson = readJsonFile(packageJsonPath);
|
|
776
|
+
if (!packageJson.name) {
|
|
777
|
+
throw new Error(`package.json in ${targetDir} must define "name".`);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
ensureNpmAvailable(deps);
|
|
781
|
+
ensureNpmAuthenticated(deps);
|
|
782
|
+
|
|
783
|
+
const summary = createSummary();
|
|
784
|
+
summary.updatedScriptKeys.push('npm.auth', 'npm.package.lookup');
|
|
785
|
+
|
|
786
|
+
if (!packageJson.publishConfig || packageJson.publishConfig.access !== 'public') {
|
|
787
|
+
summary.warnings.push('package.json publishConfig.access is not "public". First publish may fail for public packages.');
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
const existsOnNpm = packageExistsOnNpm(deps, packageJson.name);
|
|
791
|
+
if (existsOnNpm) {
|
|
792
|
+
summary.skippedScriptKeys.push('npm.first_publish');
|
|
793
|
+
} else {
|
|
794
|
+
summary.updatedScriptKeys.push('npm.first_publish_required');
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
if (!existsOnNpm && !args.publishFirst) {
|
|
798
|
+
summary.warnings.push(`package "${packageJson.name}" was not found on npm. Run "create-package-starter setup-npm --dir ${targetDir} --publish-first" to perform first publish.`);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
if (args.publishFirst) {
|
|
802
|
+
if (existsOnNpm) {
|
|
803
|
+
summary.warnings.push(`package "${packageJson.name}" already exists on npm. Skipping first publish.`);
|
|
804
|
+
} else if (args.dryRun) {
|
|
805
|
+
summary.warnings.push(`dry-run: would run "npm publish --access public" in ${targetDir}`);
|
|
806
|
+
} else {
|
|
807
|
+
const publish = deps.exec('npm', ['publish', '--access', 'public'], { cwd: targetDir });
|
|
808
|
+
if (publish.status !== 0) {
|
|
809
|
+
throw new Error(`First publish failed: ${(publish.stderr || publish.stdout || '').trim()}`);
|
|
810
|
+
}
|
|
811
|
+
summary.updatedScriptKeys.push('npm.first_publish_done');
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
summary.warnings.push('Configure npm Trusted Publisher manually in npm package settings after first publish.');
|
|
816
|
+
summary.warnings.push('Trusted Publisher requires owner, repository, workflow file (.github/workflows/release.yml), and branch (main by default).');
|
|
817
|
+
|
|
818
|
+
printSummary(`npm setup completed for ${packageJson.name}`, summary);
|
|
819
|
+
}
|
|
820
|
+
|
|
686
821
|
function setupGithub(args, dependencies = {}) {
|
|
687
822
|
const deps = {
|
|
688
823
|
exec: dependencies.exec || execCommand
|
|
@@ -750,6 +885,11 @@ async function run(argv, dependencies = {}) {
|
|
|
750
885
|
return;
|
|
751
886
|
}
|
|
752
887
|
|
|
888
|
+
if (parsed.mode === 'setup-npm') {
|
|
889
|
+
setupNpm(parsed.args, dependencies);
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
|
|
753
893
|
createNewPackage(parsed.args);
|
|
754
894
|
}
|
|
755
895
|
|
|
@@ -757,5 +897,6 @@ module.exports = {
|
|
|
757
897
|
run,
|
|
758
898
|
parseRepoFromRemote,
|
|
759
899
|
createBaseRulesetPayload,
|
|
760
|
-
setupGithub
|
|
900
|
+
setupGithub,
|
|
901
|
+
setupNpm
|
|
761
902
|
};
|