Skip to content

refactor(readConfigFile): rework config file reading and testing - #139

Merged
JuanGalilea merged 2 commits into
masterfrom
refactor/read-config
Sep 18, 2026
Merged

JuanGalilea merged 2 commits into
masterfrom
refactor/read-config

Conversation

@JuanGalilea

Copy link
Copy Markdown
Contributor

PR number one of the setup for config file reworks.

Featuring:

  • Tons of fixtures
  • clear cut point for new config file parsing
  • rework of readConfigFile passing previous mock-based test battery and new fixture-based one
  • some config fixes like .vscode and tsconfig misconfigurations

@ruocco-l ruocco-l left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some questions, but looks ok to me, thanks!

Comment thread bin/utils/actor-config.ts Outdated
* Stage 3 — normalizes the paths the user wrote and rejects entries that collide once normalized.
* The repo root is "" from here on, however it was spelled in the file.
*/
export const denormalizeConfig = (config: ActorConfigFile): DenormalizedActor[] => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it called denormalize if it normalizes (according to the JSDoc)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it normalizes the paths but the action as a whole is denormalizing (tbh i didn't read the jsdoc my bad my bad).
Will fix it

const seenFolders = new Set<string>();

return config.actors.map((entry) => {
const folder = entry.folder === '.' ? '' : stripTrailingSlash(entry.folder);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that this was already here, but i realized that you can trick this by using "./" in the config file. Can you fix it now, since we are touching on it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'll fix it in the path resolution pr (PR number 3)
Since there is quite a bit of dubious stuff around paths

});
};

it('loadActorConfig resolves an entry that never came from a config file', async () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what this is supposed to mean

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

me neither, fixed

@metalwarrior665 metalwarrior665 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The flow is nice, seems like something we could stick to long term. Just few polishing suggestions.

Comment thread bin/utils/actor-config.ts Outdated
* trace left of which config flavour produced it. Whatever replaces or extends
* {@link parseConfigFile} only has to produce this.
*/
export interface DenormalizedActor {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I think the naming will need some work. One is Actor and other is ActorConfig, so shouldn't this be DenormalizedActorConfig?
  2. Do we need this ugly term? We could also do something like ActorConfigBase -> ActorConfigComplete (or similar)? Or perhaps ActorConfigWithSchemas as that is what it does for now

@JuanGalilea JuanGalilea Sep 18, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. true
  2. i mean it is the right term. Maybe ResolvedActorConfig?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good

Comment thread bin/utils/actor-config.ts Outdated

// The repo root is spelled "" internally but "." in a config file, so report it the way a reader
// would have written it.
const displayFolder = (folder: string): string => folder || '.';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would inline this or at least define it closer to where used.

Comment thread bin/utils/actor-config.ts Outdated
// would have written it.
const displayFolder = (folder: string): string => folder || '.';

type ConfigIssue = z.ZodError<ActorConfigFile>['issues'][number];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is issue an official term? I would think more about validationError or at least validationIssue

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set to ConfigParsingIssue since its parsing* not validation whats going on

Comment thread bin/utils/actor-config.ts Outdated

switch (file.reason) {
case 'invalid-json':
throw new Error(`Config file "${CONFIG_FILE_NAME}" contains invalid JSON.`);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment we might want to support jsonc in the future

Comment thread bin/utils/files.ts Outdated
*/
export async function safeReadJsonObjectFile(path: string): Promise<JsonObjectReadResult> {
// `stat`, not `lstat`: a symlink must resolve to its target, matching what `readFile` does below.
const stats = await stat(path).catch(() => null);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use sync IO, so we don't have to propagate async everywhere. From outside async looks like we will do some network calls.

This is a sequential step in CLI, we don't need wide concurrency like a web server.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

YES!!! i did not touch it due to chesterton's fence. but it bugged me the whole time

Comment thread bin/utils/actor-config.ts Outdated
// replace these two when enabling different file structures
// some kind of strategy pattern seems appropriate here
const parsedConfigFile = parseConfigFile(rawConfigFile);
const entries = denormalizeConfig(parsedConfigFile);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again the variables don't follow. parsedConfigFile -> entries -> actorConfigs. They should derive from one another.

Comment thread bin/utils/files.ts Outdated
@@ -0,0 +1,57 @@
import { readFile, stat } from 'node:fs/promises';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

files.ts is too generic. How about json-object.ts. We can make more files for other things later

@@ -0,0 +1,251 @@
import path from 'node:path';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of having a utils dump, we could just have bin/config-parsers/. We will also add the actor.json parsing to it is already 3 related files.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i was about to do this now since i started doing groundwork for the multiple config structures and noticed it as an issue.
Will solve it here.

// Strips a trailing slash so config-declared paths ("actors/shopify/" vs "actors/shopify") compare equal.
const stripTrailingSlash = (pathValue: string): string => pathValue.replace(/\/+$/, '');

const findOverlappingContextPaths = (contextPaths: string[]): [string, string] | undefined => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Next time it would be good to add PR comment that this function was moved as is so I don't have to read it. Not sure if there is some easy hack for me to do this...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sometimes github detects it, but its really hit or miss

@JuanGalilea
JuanGalilea merged commit 3299da9 into master Sep 18, 2026
9 checks passed
@JuanGalilea
JuanGalilea deleted the refactor/read-config branch September 21, 2026 08:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants