11import { existsSync } from "fs" ;
2- import { join } from "path" ;
2+ import { join , resolve } from "path" ;
33import { warn } from "@cloudflare/cli" ;
44import { brandColor , dim } from "@cloudflare/cli/colors" ;
5- import { spinner } from "@cloudflare/cli/interactive " ;
5+ import { runCommand } from "helpers/command " ;
66import { getLatestTypesEntrypoint } from "helpers/compatDate" ;
7- import { readFile , usesTypescript , writeFile } from "helpers/files" ;
7+ import { readFile , readJSON , usesTypescript , writeFile } from "helpers/files" ;
88import { detectPackageManager } from "helpers/packageManagers" ;
99import { installPackages } from "helpers/packages" ;
1010import * as jsonc from "jsonc-parser" ;
11- import type { C3Context } from "types" ;
11+ import type { C3Context , PackageJson } from "types" ;
1212
1313/**
14- * Installs the latest version of the `@cloudflare/workers-types` package
15- * and updates the .tsconfig file to use the latest entrypoint version.
14+ * Generate types using `wrangler types` and update tsconfig
1615 */
17- export async function installWorkersTypes ( ctx : C3Context ) {
18- const { npm } = detectPackageManager ( ) ;
1916
17+ export async function generateWorkersTypes ( ctx : C3Context ) {
2018 if ( ! usesTypescript ( ctx ) ) {
2119 return ;
2220 }
21+ const packageJsonPath = resolve ( "package.json" ) ;
22+ const packageManifest = readJSON ( packageJsonPath ) as PackageJson ;
23+ if ( ! Object . keys ( packageManifest . scripts ?? { } ) . includes ( "cf-typegen" ) ) {
24+ return ;
25+ }
2326
24- await installPackages ( [ "@cloudflare/workers-types" ] , {
25- dev : true ,
26- startText : "Installing @cloudflare/workers-types" ,
27- doneText : `${ brandColor ( "installed" ) } ${ dim ( `via ${ npm } ` ) } ` ,
27+ const { npm } = detectPackageManager ( ) ;
28+
29+ const typesCmd = [ npm , "run" , "cf-typegen" ] ;
30+
31+ await runCommand ( typesCmd , {
32+ cwd : ctx . project . path ,
33+ silent : true ,
34+ env : {
35+ CLOUDFLARE_ACCOUNT_ID : ctx . account ?. id ,
36+ NODE_ENV : "production" ,
37+ } ,
38+ startText : "Generating types for your application" ,
39+ doneText : `${ brandColor ( "generated" ) } ${ dim ( `to \`${ ctx . template . typesPath ?? "worker-configuration.d.ts" } \` via \`${ typesCmd . join ( " " ) } \`` ) } ` ,
2840 } ) ;
29- await addWorkersTypesToTsConfig ( ctx ) ;
41+
42+ if ( ctx . template . compatibilityFlags ?. includes ( "nodejs_compat" ) ) {
43+ await installPackages ( [ "@types/node" ] , {
44+ dev : true ,
45+ startText : "Installing @types/node" ,
46+ doneText : `${ brandColor ( "installed" ) } ${ dim ( `via ${ npm } ` ) } ` ,
47+ } ) ;
48+ }
49+
50+ delete packageManifest [ "devDependencies" ] ?. [ "@cloudflare/workers-types" ] ;
51+
52+ writeFile ( packageJsonPath , JSON . stringify ( packageManifest , null , 2 ) ) ;
53+ await updateTsConfig ( ctx ) ;
3054}
3155
32- export async function addWorkersTypesToTsConfig ( ctx : C3Context ) {
56+ export async function updateTsConfig ( ctx : C3Context ) {
3357 const tsconfigPath = join ( ctx . project . path , "tsconfig.json" ) ;
3458 if ( ! existsSync ( tsconfigPath ) ) {
3559 return ;
3660 }
3761
38- const s = spinner ( ) ;
39- s . start ( "Adding latest types to `tsconfig.json`" ) ;
40-
4162 const tsconfig = readFile ( tsconfigPath ) ;
42- const entrypointVersion = getLatestTypesEntrypoint ( ctx ) ;
43- if ( entrypointVersion === null ) {
44- s . stop (
45- `${ brandColor (
46- "skipped" ,
47- ) } couldn't find latest compatible version of @cloudflare/workers-types`,
48- ) ;
49- return ;
50- }
51-
52- const typesEntrypoint = `@cloudflare/workers-types/${ entrypointVersion } ` ;
5363
5464 try {
5565 const config = jsonc . parse ( tsconfig ) ;
5666 const currentTypes = config . compilerOptions ?. types ?? [ ] ;
57-
58- const explicitEntrypoint = ( currentTypes as string [ ] ) . some ( ( t ) =>
59- t . match ( / @ c l o u d f l a r e \/ w o r k e r s - t y p e s \/ \d { 4 } - \d { 2 } - \d { 2 } / ) ,
60- ) ;
61-
62- // If a type declaration with an explicit entrypoint exists, leave the types as is
63- // Otherwise, add the latest entrypoint
64- const newTypes = explicitEntrypoint
65- ? [ ...currentTypes ]
66- : [
67- ...currentTypes . filter (
68- ( t : string ) => t !== "@cloudflare/workers-types" ,
69- ) ,
70- typesEntrypoint ,
71- ] ;
67+ let newTypes : string [ ] ;
68+ if ( ctx . template . installWorkersTypes ) {
69+ const entrypointVersion = getLatestTypesEntrypoint ( ctx ) ;
70+ if ( entrypointVersion === null ) {
71+ return ;
72+ }
73+ const typesEntrypoint = `@cloudflare/workers-types/${ entrypointVersion } ` ;
74+ const explicitEntrypoint = ( currentTypes as string [ ] ) . some ( ( t ) =>
75+ t . match ( / @ c l o u d f l a r e \/ w o r k e r s - t y p e s \/ \d { 4 } - \d { 2 } - \d { 2 } / ) ,
76+ ) ;
77+ // If a type declaration with an explicit entrypoint exists, leave the types as is
78+ // Otherwise, add the latest entrypoint
79+ newTypes = explicitEntrypoint
80+ ? [ ...currentTypes ]
81+ : [
82+ ...currentTypes . filter (
83+ ( t : string ) => t !== "@cloudflare/workers-types" ,
84+ ) ,
85+ typesEntrypoint ,
86+ ] ;
87+ } else {
88+ newTypes = [
89+ ...currentTypes . filter (
90+ ( t : string ) => ! t . startsWith ( "@cloudflare/workers-types" ) ,
91+ ) ,
92+ ctx . template . typesPath ?? "./worker-configuration.d.ts" ,
93+ ...( ctx . template . compatibilityFlags ?. includes ( "nodejs_compat" )
94+ ? [ "node" ]
95+ : [ ] ) ,
96+ ] ;
97+ }
98+ if ( newTypes . sort ( ) === currentTypes . sort ( ) ) {
99+ return ;
100+ }
72101
73102 // If we detect any tabs, use tabs, otherwise use spaces.
74103 // We need to pass an explicit value here in order to preserve formatting properly.
@@ -86,10 +115,24 @@ export async function addWorkersTypesToTsConfig(ctx: C3Context) {
86115 const updated = jsonc . applyEdits ( tsconfig , edits ) ;
87116 writeFile ( tsconfigPath , updated ) ;
88117 } catch ( error ) {
89- warn (
90- "Failed to update `tsconfig.json` with latest `@cloudflare/workers-types` entrypoint." ,
91- ) ;
118+ warn ( "Failed to update `tsconfig.json`." ) ;
92119 }
120+ }
93121
94- s . stop ( `${ brandColor ( "added" ) } ${ dim ( typesEntrypoint ) } ` ) ;
122+ /**
123+ * Installs the latest version of the `@cloudflare/workers-types` package
124+ * and updates the .tsconfig file to use the latest entrypoint version.
125+ */
126+ export async function installWorkersTypes ( ctx : C3Context ) {
127+ if ( ! usesTypescript ( ctx ) ) {
128+ return ;
129+ }
130+ const { npm } = detectPackageManager ( ) ;
131+
132+ await installPackages ( [ "@cloudflare/workers-types" ] , {
133+ dev : true ,
134+ startText : "Installing @cloudflare/workers-types" ,
135+ doneText : `${ brandColor ( "installed" ) } ${ dim ( `via ${ npm } ` ) } ` ,
136+ } ) ;
137+ await updateTsConfig ( ctx ) ;
95138}
0 commit comments