Skip to content

Commit d9c707a

Browse files
committed
Merge pull request #2 from hmerritt/dev
Feat: major refactor of run logic
2 parents dedd6a9 + 3bde3a8 commit d9c707a

10 files changed

Lines changed: 285 additions & 94 deletions

File tree

command/run.go

Lines changed: 64 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@ package command
22

33
import (
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 {
2221
func (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
2726
Inject 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

npm/reactenv-darwin-arm64/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@reactenv/cli-darwin-arm64",
3-
"version": "0.1.64",
3+
"version": "0.1.73",
44
"description": "The macOS ARM 64-bit binary for reactenv, an experimental solution to inject env variables after a build.",
55
"license": "Apache-2.0",
66
"os": [

npm/reactenv-darwin-x64/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@reactenv/cli-darwin-x64",
3-
"version": "0.1.64",
3+
"version": "0.1.73",
44
"description": "The macOS 64-bit binary for reactenv, an experimental solution to inject env variables after a build.",
55
"license": "Apache-2.0",
66
"os": [

npm/reactenv-linux-arm64/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@reactenv/cli-linux-arm64",
3-
"version": "0.1.64",
3+
"version": "0.1.73",
44
"description": "The Linux ARM 64-bit binary for reactenv, an experimental solution to inject env variables after a build.",
55
"license": "Apache-2.0",
66
"os": [

npm/reactenv-linux-x64/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@reactenv/cli-linux-x64",
3-
"version": "0.1.64",
3+
"version": "0.1.73",
44
"description": "The Linux 64-bit binary for reactenv, an experimental solution to inject env variables after a build.",
55
"license": "Apache-2.0",
66
"os": [

npm/reactenv-win32-x64/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@reactenv/cli-win32-x64",
3-
"version": "0.1.64",
3+
"version": "0.1.73",
44
"description": "The Windows 64-bit binary for reactenv, an experimental solution to inject env variables after a build.",
55
"license": "Apache-2.0",
66
"os": [

npm/reactenv/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@reactenv/cli",
3-
"version": "0.1.64",
3+
"version": "0.1.73",
44
"description": "reactenv, an experimental solution to inject env variables after a build.",
55
"license": "Apache-2.0",
66
"bin": {

0 commit comments

Comments
 (0)