create-three-blocks-starter 0.0.7 → 0.0.8

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 +39 -2
  2. package/bin/index.js +314 -206
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -7,9 +7,15 @@ Private CLI to scaffold a new project from the Three Blocks starter.
7
7
  Authenticate to the private registry first (one time per project):
8
8
 
9
9
  ```bash
10
- npx -y three-blocks-login --mode project --scope @three-blocks
10
+ # Using pnpm (recommended - no warnings)
11
+ pnpm dlx three-blocks-login@latest@latest --mode project --scope @three-blocks --channel stable
12
+
13
+ # Using npx (may show harmless npm config warnings)
14
+ npx -y three-blocks-login@latest --mode project --scope @three-blocks --channel stable
11
15
  ```
12
16
 
17
+ > **Note:** If you see npm warnings about unknown env configs, use `pnpm dlx` instead of `npx`, or see [three-blocks-login NPM warnings fix](https://www.npmjs.com/package/three-blocks-login).
18
+
13
19
  Then scaffold a new app:
14
20
 
15
21
  ```bash
@@ -26,6 +32,37 @@ pnpm i
26
32
  pnpm dev
27
33
  ```
28
34
 
35
+ ## CI/CD & Vercel Setup
36
+
37
+ The CLI now automatically generates the correct setup, but if you need to configure it manually:
38
+
39
+ ### Quick Setup (3 steps)
40
+
41
+ 1. **Commit `.npmrc`** to your repository (generated automatically by create-three-blocks-starter):
42
+ ```
43
+ @three-blocks:registry=https://three-blocks-196905988268.d.codeartifact.ap-northeast-1.amazonaws.com/npm/core/
44
+ ```
45
+
46
+ 2. **Preinstall script** (added automatically by create-three-blocks-starter):
47
+ ```json
48
+ {
49
+ "scripts": {
50
+ "preinstall": "npx -y three-blocks-login@latest"
51
+ }
52
+ }
53
+ ```
54
+
55
+ 3. **Set environment variable** in your CI/CD platform:
56
+ - **Vercel**: Project Settings → Environment Variables → Add `THREE_BLOCKS_SECRET_KEY`
57
+ - **GitHub Actions**: Repository Settings → Secrets → Add `THREE_BLOCKS_SECRET_KEY`
58
+ - **Other CI**: Add `THREE_BLOCKS_SECRET_KEY` secret with your license key
59
+
60
+ ### How It Works
61
+
62
+ - Committed `.npmrc` tells pnpm WHERE to find packages (no auth needed yet)
63
+ - Preinstall script fetches a short-lived token and adds it to `.npmrc`
64
+ - This solves pnpm's timing issue (it resolves packages before running preinstall)
65
+
29
66
  ## Development (monorepo)
30
67
 
31
68
  - The CLI pulls the template from `packages/three-blocks-starter/` during `prepack`.
@@ -38,6 +75,6 @@ pnpm --filter create-three-blocks-starter run dev:sync
38
75
  ## Publish
39
76
 
40
77
  ```bash
41
- npx -y three-blocks-login --mode project --scope @three-blocks
78
+ pnpm dlx three-blocks-login@latest --mode project --scope @three-blocks
42
79
  pnpm --filter create-three-blocks-starter publish --access restricted
43
80
  ```
package/bin/index.js CHANGED
@@ -36,6 +36,24 @@ const logDebug = ( msg ) => {
36
36
 
37
37
  };
38
38
 
39
+ let tempDirForCleanup = '';
40
+ const cleanupTmpDir = () => {
41
+
42
+ if ( ! tempDirForCleanup ) return;
43
+ try {
44
+
45
+ fs.rmSync( tempDirForCleanup, { recursive: true, force: true } );
46
+
47
+ } catch ( cleanupErr ) {
48
+
49
+ logDebug( `Unable to remove temp directory ${tempDirForCleanup}: ${cleanupErr?.message || cleanupErr}` );
50
+
51
+ }
52
+
53
+ tempDirForCleanup = '';
54
+
55
+ };
56
+
39
57
  class CliError extends Error {
40
58
 
41
59
  constructor( message, {
@@ -90,6 +108,7 @@ const cleanNpmEnv = ( extra = {} ) => {
90
108
 
91
109
  };
92
110
 
111
+
93
112
  const STEP_ICON = '⏺ ';
94
113
  const logInfo = ( msg ) => {
95
114
 
@@ -121,6 +140,50 @@ const logError = ( msg ) => {
121
140
 
122
141
  };
123
142
 
143
+ const appendAuthTokenLines = ( npmrcPath, authLines = [] ) => {
144
+
145
+ if ( ! authLines.length ) return false;
146
+ try {
147
+
148
+ const existing = fs.existsSync( npmrcPath ) ? fs.readFileSync( npmrcPath, 'utf8' ) : '';
149
+ const needsNewline = existing && ! existing.endsWith( os.EOL ) ? os.EOL : '';
150
+ const next = `${existing}${needsNewline}${authLines.join( os.EOL )}${os.EOL}`;
151
+ fs.writeFileSync( npmrcPath, next, { mode: 0o600 } );
152
+ return true;
153
+
154
+ } catch ( err ) {
155
+
156
+ logWarn( `Warning: could not append auth token to .npmrc: ${err?.message || err}` );
157
+ return false;
158
+
159
+ }
160
+
161
+ };
162
+
163
+ const stripAuthTokens = ( npmrcPath, hostPatterns = [] ) => {
164
+
165
+ if ( ! hostPatterns.length ) return;
166
+ try {
167
+
168
+ if ( ! fs.existsSync( npmrcPath ) ) return;
169
+ const raw = fs.readFileSync( npmrcPath, 'utf8' );
170
+ const lines = raw.split( /\r?\n/ );
171
+ const patterns = hostPatterns.map( ( host ) => new RegExp( `^\\s*\\/\\/${host.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' )}:_authToken\\s*=`, 'i' ) );
172
+ const filtered = lines.filter( ( line ) => ! patterns.some( ( re ) => re.test( line ) ) );
173
+ let end = filtered.length;
174
+ while ( end > 0 && ! filtered[ end - 1 ].trim() ) end --;
175
+ const compact = filtered.slice( 0, end ).join( os.EOL );
176
+ const finalContent = compact ? `${compact}${os.EOL}` : '';
177
+ fs.writeFileSync( npmrcPath, finalContent, { mode: 0o600 } );
178
+
179
+ } catch ( err ) {
180
+
181
+ logWarn( `Warning: could not scrub auth token from .npmrc: ${err?.message || err}` );
182
+
183
+ }
184
+
185
+ };
186
+
124
187
  const die = ( m ) => {
125
188
 
126
189
  throw new CliError( m );
@@ -405,7 +468,7 @@ const padText = ( value, width, align = 'left' ) => {
405
468
  const makeHeaderRow = ( left, right = '', leftAlign = 'left', rightAlign = 'left' ) =>
406
469
  `│${padText( left, LEFT_WIDTH, leftAlign )}│${padText( right, RIGHT_WIDTH, rightAlign )}│`;
407
470
 
408
- const HEADER_COLOR = ESC( 33 );
471
+ const HEADER_COLOR = ESC( '38;2;21;69;245' ); // threejs-blue (#1545F5)
409
472
  const CONTENT_COLOR = ESC( 90 );
410
473
  const reapplyColor = ( value, color ) => {
411
474
 
@@ -665,204 +728,205 @@ async function main() {
665
728
  const headerLicenseId = tokenMetadata?.licenseId ? String( tokenMetadata.licenseId ) : '';
666
729
 
667
730
  // 2) Pre-login in a TEMP dir to generate a temp .npmrc (no always-auth)
668
- let tmp = '';
669
- let tmpNpmrc = '';
731
+ let tmp = mkTmpDir();
732
+ tempDirForCleanup = tmp;
733
+ let tmpNpmrc = path.join( tmp, '.npmrc' );
734
+ logProgress( `Fetching short-lived token (temp .npmrc) [channel: ${channel}] ...` );
670
735
  try {
671
736
 
672
- tmp = mkTmpDir();
673
- tmpNpmrc = path.join( tmp, '.npmrc' );
674
- logProgress( `Fetching short-lived token (temp .npmrc) [channel: ${channel}] ...` );
675
- try {
676
-
677
- run( 'npx', [ '-y', LOGIN_SPEC, '--mode', 'project', '--scope', SCOPE, '--channel', channel ], {
678
- cwd: tmp,
679
- env: { ...process.env, THREE_BLOCKS_SECRET_KEY: license, THREE_BLOCKS_CHANNEL: channel },
680
- } );
737
+ run( 'npx', [ '-y', LOGIN_SPEC, '--mode', 'project', '--scope', SCOPE, '--channel', channel ], {
738
+ cwd: tmp,
739
+ env: { ...process.env, THREE_BLOCKS_SECRET_KEY: license, THREE_BLOCKS_CHANNEL: channel },
740
+ } );
681
741
 
682
- } catch ( err ) {
742
+ } catch ( err ) {
683
743
 
684
- if ( err instanceof CliError ) {
744
+ if ( err instanceof CliError ) {
685
745
 
686
- throw new CliError(
687
- 'Login failed while minting short-lived token.',
688
- {
689
- exitCode: err.exitCode,
690
- command: err.command,
691
- args: err.args,
692
- suggestion: 'Ensure the three-blocks-login CLI is reachable and your license key is valid.',
693
- cause: err,
694
- }
695
- );
746
+ throw new CliError(
747
+ 'Login failed while minting short-lived token.',
748
+ {
749
+ exitCode: err.exitCode,
750
+ command: err.command,
751
+ args: err.args,
752
+ suggestion: 'Ensure the three-blocks-login CLI is reachable and your license key is valid.',
753
+ cause: err,
754
+ }
755
+ );
696
756
 
697
- }
757
+ }
698
758
 
699
- throw err;
759
+ throw err;
700
760
 
701
- }
761
+ }
702
762
 
703
- if ( ! fs.existsSync( tmpNpmrc ) ) {
763
+ if ( ! fs.existsSync( tmpNpmrc ) ) {
704
764
 
705
- throw new CliError( 'Login failed: temp .npmrc not created.' );
765
+ throw new CliError( 'Login failed: temp .npmrc not created.' );
706
766
 
707
- }
767
+ }
708
768
 
709
769
  // Extract registry URL from temp .npmrc so we can pass it explicitly to npm create
710
770
  let registryUrl = '';
771
+ const tempAuthLines = [];
772
+ const tempAuthHosts = new Set();
711
773
  try {
712
774
 
713
775
  const txt = fs.readFileSync( tmpNpmrc, 'utf8' );
714
- const m = txt.match( /^@[^:]+:registry=(.+)$/m );
715
- if ( m && m[ 1 ] ) registryUrl = m[ 1 ].trim();
776
+ const lines = txt.split( /\r?\n/ );
777
+ for ( const rawLine of lines ) {
716
778
 
717
- } catch ( readErr ) {
779
+ const line = rawLine.replace( /\r/g, '' );
780
+ if ( ! line ) continue;
781
+ if ( ! registryUrl ) {
718
782
 
719
- logDebug( `Could not read temp .npmrc: ${readErr?.message || readErr}` );
720
-
721
- }
722
-
723
- if ( ! headerRegistry && registryUrl ) headerRegistry = registryUrl;
724
-
725
- // 3) Scaffold the private starter by packing and extracting the tarball (avoids npm create naming transform)
726
- const starterSpec = `${STARTER_PKG}@${channel === 'stable' ? 'latest' : channel}`;
727
- logProgress( `Fetching starter tarball ${starterSpec} ...` );
728
- const createEnv = {
729
- ...process.env,
730
- THREE_BLOCKS_SECRET_KEY: license,
731
- NPM_CONFIG_USERCONFIG: tmpNpmrc,
732
- npm_config_userconfig: tmpNpmrc,
733
- };
734
- const packArgs = [ 'pack', starterSpec, '--json' ];
735
- if ( ! DEBUG ) packArgs.push( '--silent' );
736
- let packedOut = '';
737
- try {
783
+ const m = line.match( /^@[^:]+:registry=(.+)$/ );
784
+ if ( m && m[ 1 ] ) registryUrl = m[ 1 ].trim();
738
785
 
739
- packedOut = exec( 'npm', packArgs, { cwd: tmp, env: createEnv } );
786
+ }
740
787
 
741
- } catch ( err ) {
788
+ const authMatch = line.match( /^\/\/([^:]+):_authToken=.+$/ );
789
+ if ( authMatch && authMatch[ 1 ] ) {
742
790
 
743
- if ( err instanceof CliError ) {
791
+ tempAuthLines.push( line );
792
+ tempAuthHosts.add( authMatch[ 1 ] );
744
793
 
745
- const baseMessage = `Failed to fetch starter tarball ${starterSpec}.`;
746
- const detail = ( err.stderr || err.stdout || '' ).trim();
747
- const message = detail ? baseMessage : `${baseMessage} ${err.message || ''}`.trim();
748
- throw new CliError(
749
- message,
750
- {
751
- exitCode: err.exitCode,
752
- command: err.command,
753
- args: err.args,
754
- stdout: err.stdout,
755
- stderr: err.stderr,
756
- suggestion: 'Confirm your license has access to the selected channel and that the package version exists.',
757
- cause: err,
758
- }
759
- );
794
+ }
760
795
 
761
796
  }
762
797
 
763
- throw new CliError(
764
- `Failed to fetch starter tarball ${starterSpec}.`,
765
- { cause: err }
766
- );
767
-
768
- }
769
-
770
- let tarName = '';
771
- let starterVersion = '';
772
- try {
773
-
774
- const info = JSON.parse( packedOut );
775
- if ( Array.isArray( info ) ) {
776
-
777
- tarName = info[ 0 ]?.filename || '';
778
- starterVersion = info[ 0 ]?.version || '';
779
-
780
- } else {
798
+ } catch ( readErr ) {
781
799
 
782
- tarName = info.filename || '';
783
- starterVersion = info.version || '';
800
+ logDebug( `Could not read temp .npmrc: ${readErr?.message || readErr}` );
784
801
 
785
- }
802
+ }
786
803
 
787
- } catch {
804
+ if ( ! headerRegistry && registryUrl ) headerRegistry = registryUrl;
805
+ const authHostList = Array.from( tempAuthHosts );
806
+
807
+ // 3) Scaffold the private starter by packing and extracting the tarball (avoids npm create naming transform)
808
+ const starterSpec = `${STARTER_PKG}@${channel === 'stable' ? 'latest' : channel}`;
809
+ logProgress( `Fetching starter tarball ${starterSpec} ...` );
810
+ const createEnv = {
811
+ ...process.env,
812
+ THREE_BLOCKS_SECRET_KEY: license,
813
+ NPM_CONFIG_USERCONFIG: tmpNpmrc,
814
+ npm_config_userconfig: tmpNpmrc,
815
+ };
816
+ const packArgs = [ 'pack', starterSpec, '--json' ];
817
+ if ( ! DEBUG ) packArgs.push( '--silent' );
818
+ let packedOut = '';
819
+ try {
788
820
 
789
- const lines = String( packedOut || '' ).trim().split( /\r?\n/ );
790
- tarName = lines[ lines.length - 1 ] || '';
821
+ packedOut = exec( 'npm', packArgs, { cwd: tmp, env: createEnv } );
791
822
 
792
- }
823
+ } catch ( err ) {
793
824
 
794
- const tarPath = path.join( tmp, tarName );
795
- if ( ! tarName || ! fs.existsSync( tarPath ) ) {
825
+ if ( err instanceof CliError ) {
796
826
 
827
+ const baseMessage = `Failed to fetch starter tarball ${starterSpec}.`;
828
+ const detail = ( err.stderr || err.stdout || '' ).trim();
829
+ const message = detail ? baseMessage : `${baseMessage} ${err.message || ''}`.trim();
797
830
  throw new CliError(
798
- `Failed to fetch starter tarball ${starterSpec}.`,
799
- { suggestion: 'Ensure the requested release is published and accessible for your channel.' }
831
+ message,
832
+ {
833
+ exitCode: err.exitCode,
834
+ command: err.command,
835
+ args: err.args,
836
+ stdout: err.stdout,
837
+ stderr: err.stderr,
838
+ suggestion: 'Confirm your license has access to the selected channel and that the package version exists.',
839
+ cause: err,
840
+ }
800
841
  );
801
842
 
802
843
  }
803
844
 
804
- const headerStarterVersion = starterVersion || extractVersionFromTarball( tarName );
805
- const headerCoreVersion = headerChannel === 'stable' ? 'latest' : headerChannel;
806
- renderHeader( {
807
- starterVersion: headerStarterVersion,
808
- userDisplayName,
809
- planName,
810
- repoPath,
811
- projectName: appName,
812
- channel: headerChannel,
813
- coreVersion: headerCoreVersion,
814
- license,
815
- registry: headerRegistry,
816
- domain: headerDomain,
817
- region: headerRegion,
818
- repository: headerRepository,
819
- expiresAt: headerExpires,
820
- teamId: headerTeamId,
821
- teamName: headerTeamName,
822
- licenseId: headerLicenseId,
823
- } );
824
- logProgress( 'Extracting files ...' );
825
- try {
845
+ throw new CliError(
846
+ `Failed to fetch starter tarball ${starterSpec}.`,
847
+ { cause: err }
848
+ );
826
849
 
827
- run( 'tar', [ '-xzf', tarPath, '-C', targetDir, '--strip-components=1' ] );
850
+ }
828
851
 
829
- } catch ( err ) {
852
+ let tarName = '';
853
+ let starterVersion = '';
854
+ try {
830
855
 
831
- if ( err instanceof CliError ) {
856
+ const info = JSON.parse( packedOut );
857
+ if ( Array.isArray( info ) ) {
832
858
 
833
- throw new CliError(
834
- 'Failed to extract starter files.',
835
- {
836
- exitCode: err.exitCode,
837
- command: err.command,
838
- args: err.args,
839
- suggestion: 'Check file permissions and available disk space before retrying.',
840
- cause: err,
841
- }
842
- );
859
+ tarName = info[ 0 ]?.filename || '';
860
+ starterVersion = info[ 0 ]?.version || '';
843
861
 
844
- }
862
+ } else {
845
863
 
846
- throw err;
864
+ tarName = info.filename || '';
865
+ starterVersion = info.version || '';
847
866
 
848
867
  }
849
868
 
850
- } finally {
869
+ } catch {
851
870
 
852
- if ( tmp ) {
871
+ const lines = String( packedOut || '' ).trim().split( /\r?\n/ );
872
+ tarName = lines[ lines.length - 1 ] || '';
853
873
 
854
- try {
874
+ }
855
875
 
856
- fs.rmSync( tmp, { recursive: true, force: true } );
876
+ const tarPath = path.join( tmp, tarName );
877
+ if ( ! tarName || ! fs.existsSync( tarPath ) ) {
857
878
 
858
- } catch ( cleanupErr ) {
879
+ throw new CliError(
880
+ `Failed to fetch starter tarball ${starterSpec}.`,
881
+ { suggestion: 'Ensure the requested release is published and accessible for your channel.' }
882
+ );
859
883
 
860
- logDebug( `Unable to remove temp directory ${tmp}: ${cleanupErr?.message || cleanupErr}` );
884
+ }
861
885
 
862
- }
886
+ const headerStarterVersion = starterVersion || extractVersionFromTarball( tarName );
887
+ const headerCoreVersion = headerChannel === 'stable' ? 'latest' : headerChannel;
888
+ renderHeader( {
889
+ starterVersion: headerStarterVersion,
890
+ userDisplayName,
891
+ planName,
892
+ repoPath,
893
+ projectName: appName,
894
+ channel: headerChannel,
895
+ coreVersion: headerCoreVersion,
896
+ license,
897
+ registry: headerRegistry,
898
+ domain: headerDomain,
899
+ region: headerRegion,
900
+ repository: headerRepository,
901
+ expiresAt: headerExpires,
902
+ teamId: headerTeamId,
903
+ teamName: headerTeamName,
904
+ licenseId: headerLicenseId,
905
+ } );
906
+ logProgress( 'Extracting files ...' );
907
+ try {
908
+
909
+ run( 'tar', [ '-xzf', tarPath, '-C', targetDir, '--strip-components=1' ] );
910
+
911
+ } catch ( err ) {
912
+
913
+ if ( err instanceof CliError ) {
914
+
915
+ throw new CliError(
916
+ 'Failed to extract starter files.',
917
+ {
918
+ exitCode: err.exitCode,
919
+ command: err.command,
920
+ args: err.args,
921
+ suggestion: 'Check file permissions and available disk space before retrying.',
922
+ cause: err,
923
+ }
924
+ );
863
925
 
864
926
  }
865
927
 
928
+ throw err;
929
+
866
930
  }
867
931
 
868
932
  // 4) Write .env.local and .gitignore entries
@@ -876,7 +940,8 @@ async function main() {
876
940
 
877
941
  } catch {}
878
942
 
879
- for ( const line of [ '.env.local', '.npmrc' ] ) {
943
+ // Only add .env.local to .gitignore (NOT .npmrc - we want to commit the registry config)
944
+ for ( const line of [ '.env.local' ] ) {
880
945
 
881
946
  if ( ! gi.includes( line ) ) gi += ( gi.endsWith( '\n' ) || gi === '' ? '' : '\n' ) + line + '\n';
882
947
 
@@ -884,9 +949,29 @@ async function main() {
884
949
 
885
950
  await fsp.writeFile( giPath, gi );
886
951
 
887
- // 5) First install the starter already has:
888
- // "preinstall": "npx -y three-blocks-login --mode project --scope @three-blocks"
889
- // so it will mint a fresh token and write a project .npmrc.
952
+ // Create base .npmrc with registry URL (tokens added dynamically)
953
+ const npmrcPath = path.join( targetDir, '.npmrc' );
954
+ const npmrcContent = [
955
+ '# Three Blocks registry configuration',
956
+ '# Auth tokens are appended automatically by three-blocks-login and expire quickly.',
957
+ '# Avoid committing auth tokens; rerun pnpm install to refresh access if needed.',
958
+ '',
959
+ `${SCOPE}:registry=${headerRegistry || registryUrl}`,
960
+ '',
961
+ '# Auth tokens are appended during installs/preinstall hooks.',
962
+ '# DO NOT hard-code long-lived tokens in this file.',
963
+ ''
964
+ ].join( '\n' );
965
+ await fsp.writeFile( npmrcPath, npmrcContent ).catch( ( e ) => {
966
+
967
+ logWarn( `Warning: could not write .npmrc: ${e?.message || String( e )}` );
968
+
969
+ } );
970
+ const primedAuth = appendAuthTokenLines( npmrcPath, tempAuthLines );
971
+ if ( primedAuth ) logDebug( 'Primed .npmrc with short-lived auth token.' );
972
+
973
+ // 5) Add simplified preinstall script
974
+ // The script will automatically append auth token to existing .npmrc
890
975
  try {
891
976
 
892
977
  const pkgPath = path.join( targetDir, 'package.json' );
@@ -895,12 +980,13 @@ async function main() {
895
980
  pkg.scripts = pkg.scripts || {};
896
981
  if ( ! pkg.scripts.preinstall ) {
897
982
 
898
- pkg.scripts.preinstall = `npx -y ${LOGIN_CLI}@latest --mode project --scope ${SCOPE} --channel ${channel}`;
983
+ // Simplified: defaults to --mode project in CI, --scope @three-blocks, --channel stable
984
+ pkg.scripts.preinstall = `npx -y ${LOGIN_CLI}@latest`;
899
985
 
900
986
  }
901
987
 
902
988
  await fsp.writeFile( pkgPath, JSON.stringify( pkg, null, 2 ) );
903
- logSuccess( 'Added preinstall to generated package.json to refresh token on installs.' );
989
+ logSuccess( 'Added preinstall script to refresh auth tokens on installs.' );
904
990
 
905
991
  } catch ( e ) {
906
992
 
@@ -908,61 +994,77 @@ async function main() {
908
994
 
909
995
  }
910
996
 
911
- logProgress( 'Installing dependencies (preinstall will refresh token) ...' );
997
+ const installMessage = primedAuth
998
+ ? 'Installing dependencies (token primed; future installs refresh automatically) ...'
999
+ : 'Installing dependencies (preinstall will refresh token) ...';
1000
+ logProgress( installMessage );
912
1001
  const hasPnpm = spawnSync( 'pnpm', [ '-v' ], { stdio: 'ignore', env: cleanNpmEnv() } ).status === 0;
1002
+ const sharedInstallEnv = {
1003
+ ...process.env,
1004
+ THREE_BLOCKS_SECRET_KEY: license,
1005
+ THREE_BLOCKS_CHANNEL: channel,
1006
+ };
1007
+ if ( primedAuth ) sharedInstallEnv.THREE_BLOCKS_LOGIN_SKIP = '1';
1008
+ const cleanupAuthAfterBootstrap = () => {
1009
+
1010
+ if ( primedAuth && authHostList.length ) stripAuthTokens( npmrcPath, authHostList );
1011
+
1012
+ };
1013
+
913
1014
  try {
914
1015
 
915
- run( hasPnpm ? 'pnpm' : 'npm', [ hasPnpm ? 'install' : 'ci' ], {
916
- cwd: targetDir,
917
- env: {
918
- ...process.env,
919
- THREE_BLOCKS_SECRET_KEY: license,
920
- THREE_BLOCKS_CHANNEL: channel,
921
- },
922
- } );
1016
+ try {
923
1017
 
924
- } catch ( err ) {
1018
+ run( hasPnpm ? 'pnpm' : 'npm', [ hasPnpm ? 'install' : 'ci' ], {
1019
+ cwd: targetDir,
1020
+ env: sharedInstallEnv,
1021
+ } );
925
1022
 
926
- if ( err instanceof CliError ) {
1023
+ } catch ( err ) {
927
1024
 
928
- throw new CliError(
929
- 'Dependency installation failed.',
930
- {
931
- exitCode: err.exitCode,
932
- command: err.command,
933
- args: err.args,
934
- stdout: err.stdout,
935
- stderr: err.stderr,
936
- suggestion: 'Inspect the log above for npm/pnpm errors or retry with --debug for full output.',
937
- cause: err,
938
- }
939
- );
1025
+ if ( err instanceof CliError ) {
1026
+
1027
+ throw new CliError(
1028
+ 'Dependency installation failed.',
1029
+ {
1030
+ exitCode: err.exitCode,
1031
+ command: err.command,
1032
+ args: err.args,
1033
+ stdout: err.stdout,
1034
+ stderr: err.stderr,
1035
+ suggestion: 'Inspect the log above for npm/pnpm errors or retry with --debug for full output.',
1036
+ cause: err,
1037
+ }
1038
+ );
1039
+
1040
+ }
1041
+
1042
+ throw err;
940
1043
 
941
1044
  }
942
1045
 
943
- throw err;
1046
+ {
944
1047
 
945
- }
1048
+ const coreSpec = `@three-blocks/core@${channel === 'stable' ? 'latest' : channel}`;
1049
+ logProgress( `Installing ${coreSpec} ...` );
1050
+ const addArgs = hasPnpm ? [ 'add', '--save-exact', coreSpec ] : [ 'install', '--save-exact', coreSpec ];
1051
+ const r = spawnSync( hasPnpm ? 'pnpm' : 'npm', addArgs, {
1052
+ stdio: 'inherit',
1053
+ cwd: targetDir,
1054
+ env: cleanNpmEnv( sharedInstallEnv ),
1055
+ } );
1056
+ if ( r.status !== 0 ) {
946
1057
 
947
- {
948
-
949
- const coreSpec = `@three-blocks/core@${channel === 'stable' ? 'latest' : channel}`;
950
- logProgress( `Installing ${coreSpec} ...` );
951
- const addArgs = hasPnpm ? [ 'add', '--save-exact', coreSpec ] : [ 'install', '--save-exact', coreSpec ];
952
- const r = spawnSync( hasPnpm ? 'pnpm' : 'npm', addArgs, {
953
- stdio: 'inherit',
954
- cwd: targetDir,
955
- env: cleanNpmEnv( {
956
- THREE_BLOCKS_SECRET_KEY: license,
957
- THREE_BLOCKS_CHANNEL: channel,
958
- } ),
959
- } );
960
- if ( r.status !== 0 ) {
1058
+ logWarn( `Warning: could not install @three-blocks/core (exit ${r.status}).` );
961
1059
 
962
- logWarn( `Warning: could not install @three-blocks/core (exit ${r.status}).` );
1060
+ }
963
1061
 
964
1062
  }
965
1063
 
1064
+ } finally {
1065
+
1066
+ cleanupAuthAfterBootstrap();
1067
+
966
1068
  }
967
1069
 
968
1070
  console.log( '' );
@@ -978,32 +1080,38 @@ async function main() {
978
1080
 
979
1081
  }
980
1082
 
981
- main().catch( ( err ) => {
1083
+ main()
1084
+ .catch( ( err ) => {
982
1085
 
983
- if ( err instanceof CliError ) {
1086
+ if ( err instanceof CliError ) {
984
1087
 
985
- logError( err.message );
986
- const detail = ( err.stderr || err.stdout || '' ).trim();
987
- if ( detail ) {
1088
+ logError( err.message );
1089
+ const detail = ( err.stderr || err.stdout || '' ).trim();
1090
+ if ( detail ) {
988
1091
 
989
- const lines = detail.split( /\r?\n/ );
990
- for ( const line of lines ) console.error( dim( ` ${line}` ) );
1092
+ const lines = detail.split( /\r?\n/ );
1093
+ for ( const line of lines ) console.error( dim( ` ${line}` ) );
991
1094
 
992
- }
1095
+ }
1096
+
1097
+ if ( err.suggestion ) logInfo( dim( `Hint: ${err.suggestion}` ) );
1098
+ if ( DEBUG && err.cause && err.cause !== err && err.cause?.stack ) {
1099
+
1100
+ console.error( dim( err.cause.stack ) );
993
1101
 
994
- if ( err.suggestion ) logInfo( dim( `Hint: ${err.suggestion}` ) );
995
- if ( DEBUG && err.cause && err.cause !== err && err.cause?.stack ) {
1102
+ }
996
1103
 
997
- console.error( dim( err.cause.stack ) );
1104
+ process.exit( err.exitCode ?? 1 );
998
1105
 
999
1106
  }
1000
1107
 
1001
- process.exit( err.exitCode ?? 1 );
1108
+ const fallback = err?.stack || err?.message || String( err );
1109
+ logError( fallback );
1110
+ process.exit( 1 );
1002
1111
 
1003
- }
1112
+ } )
1113
+ .finally( () => {
1004
1114
 
1005
- const fallback = err?.stack || err?.message || String( err );
1006
- logError( fallback );
1007
- process.exit( 1 );
1115
+ cleanupTmpDir();
1008
1116
 
1009
- } );
1117
+ } );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-three-blocks-starter",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Create a new Three Blocks starter app",