@@ -2,12 +2,11 @@ package command
22
33import (
44 "fmt"
5- "io/fs"
65 "os"
7- "path"
86 "regexp"
97 "strings"
108
9+ "github.com/hmerritt/reactenv/reactenv"
1110 "github.com/hmerritt/reactenv/ui"
1211)
1312
@@ -22,7 +21,7 @@ func (c *RunCommand) Synopsis() string {
2221func (c * RunCommand ) Help () string {
2322 jsInfo := c .UI .Colorize (".js" , c .UI .InfoColor )
2423 helpText := fmt .Sprintf (`
25- Usage: reactenv run [options] ASSET_PATH
24+ Usage: reactenv run [options] PATH
2625
2726Inject environment variables into a built react app.
2827
@@ -33,7 +32,7 @@ Example:
3332 ├── index.css
3433 ├── index-csxw0qbp%s
3534 ├── login.lazy-b839zm%s
36- └── user.lazy-c7942lh%s <- Runs on all %s files in ASSET_PATH
35+ └── user.lazy-c7942lh%s <- Runs on all %s files in PATH
3736` , jsInfo , jsInfo , jsInfo , jsInfo )
3837
3938 return strings .TrimSpace (helpText )
@@ -49,116 +48,94 @@ func (c *RunCommand) Run(args []string) int {
4948 args = c .Flags ().Parse (c .UI , args )
5049
5150 if len (args ) == 0 {
52- c .UI .Error ("No asset path entered." )
51+ c .UI .Error ("No asset PATH entered." )
5352 c .exitWithHelp ()
5453 }
5554
5655 pathToAssets := args [0 ]
5756
5857 if _ , err := os .Stat (pathToAssets ); os .IsNotExist (err ) {
59- c .UI .Error (fmt .Sprintf ("Asset path '%s' does not exist." , pathToAssets ))
58+ c .UI .Error (fmt .Sprintf ("File PATH '%s' does not exist." , pathToAssets ))
6059 c .exitWithHelp ()
6160 }
6261
63- // Find all .js files
64- files , err := os . ReadDir ( pathToAssets )
65- javascriptFiles := make ([]fs. DirEntry , 0 , len ( files ) )
62+ // @TODO: Add flag to specify matcher
63+ fileMatchExpression := `.*\.js`
64+ _ , err := regexp . Compile ( fileMatchExpression )
6665
6766 if err != nil {
68- c .UI .Error (fmt .Sprintf ("Error when reading asset directory files .\n " ))
67+ c .UI .Error (fmt .Sprintf ("File match expression '%s' is not valid .\n " , fileMatchExpression ))
6968 c .UI .Error (fmt .Sprintf ("%v" , err ))
70- os . Exit ( 1 )
69+ c . exitWithHelp ( )
7170 }
7271
73- for _ , file := range files {
74- if file .IsDir () || ! strings .HasSuffix (file .Name (), ".js" ) {
75- continue
76- }
77- javascriptFiles = append (javascriptFiles , file )
78- }
72+ renv := reactenv .NewReactenv (c .UI )
7973
80- if len (javascriptFiles ) == 0 {
81- c .UI .Error ("No JavaScript (.js) files found in asset directory." )
74+ err = renv .FindFiles (pathToAssets , fileMatchExpression )
75+
76+ if err != nil {
77+ c .UI .Error (fmt .Sprintf ("Error reading files in PATH '%s'.\n " , pathToAssets ))
78+ c .UI .Error (fmt .Sprintf ("%v" , err ))
8279 os .Exit (1 )
8380 }
8481
85- allOccurrences := 0
86-
87- // @TODO:
88- // - Loop all JS files, find all occurrences
89- // - Log: Total occurrences, Each file occurrences, All found occurrences + the ENV value to be injected
90- // - Re-loop all JS files, replace all occurrences
91- // - Log errors, warnings and successes
92-
93- // Inject environment variables into .js files
94- for _ , file := range javascriptFiles {
95- // Read .js file
96- filePath := path .Join (pathToAssets , file .Name ())
97- fileContents , err := os .ReadFile (filePath )
98- fileContentsNew := make ([]byte , 0 , len (fileContents ))
99-
100- if err != nil {
101- c .UI .Error (fmt .Sprintf ("Error when reading file '%s'.\n " , file .Name ()))
102- c .UI .Error (fmt .Sprintf ("%v" , err ))
103- os .Exit (1 )
104- }
105-
106- // Find every occurrence of `reactenv.`
107- occurrences := regexp .MustCompile (`(__reactenv\.[a-zA-Z_$][0-9a-zA-Z_$]*)` ).FindAllStringIndex (string (fileContents ), - 1 )
108- occurrenceReplacementValues := make ([]string , len (occurrences ))
109-
110- if len (occurrences ) == 0 {
111- continue
112- }
113-
114- allOccurrences += len (occurrences )
115-
116- // For each occurrence, find the corresponding environment variable,
117- // exits if any environment variable is not set.
118- for index , occurrence := range occurrences {
119- occurrenceText := string (fileContents [occurrence [0 ]:occurrence [1 ]])
120- envName := strings .Replace (occurrenceText , "__reactenv." , "" , 1 )
121- envValue , envExists := os .LookupEnv (envName )
122-
123- if ! envExists {
124- c .UI .Error (fmt .Sprintf ("Environment variable not set: %s" , envName ))
125- os .Exit (1 )
126- }
127-
128- occurrenceReplacementValues [index ] = envValue
129- }
130-
131- // Run replacement of all occurrences
132- lastIndex := 0
133- for index , occurrence := range occurrences {
134- envValue := occurrenceReplacementValues [index ]
135- start , end := occurrence [0 ], occurrence [1 ]
136-
137- fileContentsNew = append (fileContentsNew , fileContents [lastIndex :start ]... )
138- fileContentsNew = append (fileContentsNew , envValue ... )
139- lastIndex = end
140- }
141- fileContentsNew = append (fileContentsNew , fileContents [lastIndex :]... )
142-
143- // Write .js file
144- if err := os .WriteFile (filePath , fileContentsNew , 0644 ); err != nil {
145- c .UI .Error (fmt .Sprintf ("Error when writing to file '%s'.\n " , filePath ))
146- c .UI .Error (fmt .Sprintf ("%v" , err ))
147- os .Exit (1 )
148- }
82+ if len (renv .Files ) == 0 {
83+ c .UI .Error (fmt .Sprintf ("No files found in path '%s' using matcher '%s'" , pathToAssets , fileMatchExpression ))
84+ os .Exit (1 )
14985 }
15086
151- if allOccurrences == 0 {
152- c .UI .Warn (ui .WrapAtLength (fmt .Sprintf ("No reactenv environment variables were found in any of the .js files within '%s', therefore nothing was injected.\n " , pathToAssets ), 0 ))
87+ renv .FindOccurrences ()
88+
89+ if renv .OccurrencesTotal == 0 {
90+ c .UI .Warn (ui .WrapAtLength (fmt .Sprintf ("No reactenv environment variables were found in any of the %d '%s' files within '%s', therefore nothing was injected.\n " , renv .FilesMatchTotal , fileMatchExpression , pathToAssets ), 0 ))
15391 c .UI .Warn (ui .WrapAtLength ("Possible causes:" , 4 ))
92+ c .UI .Warn (ui .WrapAtLength (" - reactenv has already ran on these files" , 4 ))
15493 c .UI .Warn (ui .WrapAtLength (" - Environment variables were not replaced with `__reactenv.<name>` during build" , 4 ))
155- c .UI .Warn (ui .WrapAtLength (" - 'reactenv' has already ran on these files" , 4 ))
15694 c .UI .Warn ("" )
15795 duration .In (c .UI .WarnColor , "" )
15896 return 1
15997 }
16098
161- duration .In (c .UI .SuccessColor , fmt .Sprintf ("Injected '%d' environment variables" , allOccurrences ))
99+ c .UI .Output (
100+ fmt .Sprintf (
101+ "Found %d reactenv environment %s in %d/%d matching files:" ,
102+ renv .OccurrencesTotal ,
103+ ui .Pluralize ("variable" , renv .OccurrencesTotal ),
104+ len (renv .Files ),
105+ renv .FilesMatchTotal ,
106+ ),
107+ )
108+ for fileIndex , fileOccurrencesTotal := range renv .OccurrencesByFile {
109+ c .UI .Output (
110+ fmt .Sprintf (
111+ " - %4dx in %s" ,
112+ len (fileOccurrencesTotal .Occurrences ),
113+ (* renv .Files [fileIndex ]).Name (),
114+ ),
115+ )
116+ }
117+ c .UI .Output ("" )
118+
119+ c .UI .Output (fmt .Sprintf ("Environment %s checklist (ticked if value has been set):" , ui .Pluralize ("variable" , renv .OccurrencesTotal )))
120+ envValuesMissing := 0
121+ for occurrenceKey := range renv .OccurrenceKeys {
122+ check := "✅"
123+ if _ , ok := renv .OccurrenceKeysReplacement [occurrenceKey ]; ! ok {
124+ check = "❌"
125+ envValuesMissing ++
126+ }
127+ c .UI .Output (fmt .Sprintf (" - %4s %s" , check , occurrenceKey ))
128+ }
129+ c .UI .Output ("" )
130+
131+ if envValuesMissing > 0 {
132+ c .UI .Error (fmt .Sprintf ("Environment %s not set. See above checklist for missing values." , ui .Pluralize ("variable" , envValuesMissing )))
133+ os .Exit (1 )
134+ }
135+
136+ renv .ReplaceOccurrences ()
137+
138+ duration .In (c .UI .SuccessColor , fmt .Sprintf ("Injected all environment variables" ))
162139 return 0
163140}
164141
0 commit comments