Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ yarn plugin import https://raw.githubusercontent.com/toss/yarn-plugin-catalogs/m
options:
default: [stable] # Optional: Default catalog groups for 'yarn add'
validation: warn # Optional: 'warn' | 'strict' | 'off'
includedWorkspaces: [] # Optional: Workspaces to include
ignoredWorkspaces: [] # Optional: Workspaces to ignore
includedWorkspaces: [] # Optional: Workspaces to include
ignoredWorkspaces: [] # Optional: Workspaces to ignore

list:
root: # Special alias for the root catalog (accessed via catalog:)
Expand Down Expand Up @@ -262,7 +262,7 @@ If your `package.json` has more `catalog:stable` dependencies than `catalog:beta

Control which workspaces are included in catalog protocol processing using `includedWorkspaces` and `ignoredWorkspaces` options.

#### Including Workspaces
#### Including Workspaces

Use `includedWorkspaces` to specify which workspaces should be processed for catalogs. Only matching workspaces will receive catalog validation and default catalog auto-application:

Expand Down Expand Up @@ -309,7 +309,7 @@ Control how strictly catalog usage is enforced:
```yaml
# In catalogs.yml
options:
validation: warn # 'warn' | 'strict' | 'off'
validation: warn # 'warn' | 'strict' | 'off' or a per-catalog map (see below)

list:
stable:
Expand Down Expand Up @@ -364,6 +364,25 @@ In this example:
- `react` will use `strict` validation (from `stable/canary`)
- `lodash` will use `strict` validation (from `stable/canary` since it's not in parent)

#### Skipping Validation for Specific Workspaces

Use `noValidationWorkspaces` to bypass validation for certain workspaces:

```yaml
# In catalogs.yml
options:
validation: strict
includedWorkspaces: [apps-*]
ignoredWorkspaces: [apps-legacy]
noValidationWorkspaces: [apps-demo]

list:
stable:
react: npm:18.0.0
```

`apps-demo` will skip validation entirely (even though validation is `strict`), while `apps-legacy` stays ignored and still won't be validated.

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.
Expand Down
100 changes: 50 additions & 50 deletions bundles/@yarnpkg/plugin-catalogs.js

Large diffs are not rendered by default.

66 changes: 66 additions & 0 deletions sources/__tests__/validation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,4 +394,70 @@ describe("validation", () => {

await expect(workspace.yarn.install()).rejects.toThrow();
});

describe("per-workspace validation overrides", () => {
it("should apply workspace filters before handling validation", async () => {
workspace = await createTestWorkspace();

await workspace.writeCatalogsYml({
options: {
default: ["root"],
includedWorkspaces: ["workspace-*"],
ignoredWorkspaces: ["workspace-ignored"],
noValidationWorkspaces: ["workspace-*"],
},
list: {
root: {
react: "npm:18.0.0",
},
},
});

await workspace.yarn.catalogs.apply();

await workspace.writeJson("package.json", {
name: "workspace-ignored",
version: "1.0.0",
private: true,
dependencies: {},
});

const { stderr } = await workspace.yarn.add("react@17.0.0");
expect(stderr).toBe("");

expect(await hasDependency(workspace, "react@npm:17.0.0")).toBe(true);
});

it("should skip validation even when validation is strict", async () => {
workspace = await createTestWorkspace();

await workspace.writeCatalogsYml({
options: {
validation: "strict",
noValidationWorkspaces: ["workspace-no-validation"],
},
list: {
stable: {
react: "npm:18.0.0",
},
},
});

await workspace.yarn.catalogs.apply();

await workspace.writeJson("package.json", {
name: "workspace-no-validation",
version: "1.0.0",
private: true,
dependencies: {
react: "17.0.0",
},
});

const { stderr } = await workspace.yarn.install();
expect(stderr).toBe("");

expect(await hasDependency(workspace, "react@npm:17.0.0")).toBe(true);
});
});
});
20 changes: 20 additions & 0 deletions sources/configuration/reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,26 @@ export class CatalogsConfigurationReader {
return false;
}

/**
* Check if a workspace should skip validation based on catalogs.yml
* configuration
*/
async shouldSkipWorkspaceValidation(workspace: Workspace): Promise<boolean> {
if (!workspace.manifest.name) return false;

const catalogsYml = await this.read(workspace.project);
const options = catalogsYml?.options;
if (!options) return false;
const workspaceName = structUtils.stringifyIdent(workspace.manifest.name);

if (options.noValidationWorkspaces) {
const noValidationRequired = isMatch(workspaceName, options.noValidationWorkspaces);
if (noValidationRequired) return true;
}

return false;
}

/**
* Write catalogs to .yarnrc.yml in Yarn's native format
* This applies catalogs.yml to .yarnrc.yml
Expand Down
24 changes: 22 additions & 2 deletions sources/configuration/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,24 @@
]
},
"validation": {
"type": "string",
"description": "The level of dependency source validation",
"enum": ["warn", "strict", "off"]
"oneOf": [
{
"type": "string",
"enum": ["warn", "strict", "off"]
},
{
"type": "object",
"additionalProperties": false,
"propertyNames": { "$ref": "#/$defs/catalogGroupName" },
"patternProperties": {
".*": {
"type": "string",
"enum": ["warn", "strict", "off"]
}
}
}
]
},
"includedWorkspaces": {
"type": "array",
Expand All @@ -35,6 +50,11 @@
"type": "array",
"description": "The list of workspace names/paths to exclude from catalog application",
"items": { "$ref": "#/$defs/workspaceName" }
},
"noValidationWorkspaces": {
"type": "array",
"description": "The list of workspace names/paths to bypass validation regardless of validation settings",
"items": { "$ref": "#/$defs/workspaceName" }
}
}
},
Expand Down
12 changes: 12 additions & 0 deletions sources/configuration/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ interface CatalogsOptions {
* Workspaces matching these patterns will be excluded from catalog processing, regardless of `includedWorkspaces`
*/
ignoredWorkspaces?: string[];
/**
* List of workspaces to exclude from validation (opt-out) Workspaces matching
* these patterns will be excluded from catalog validation, regardless of
* `validation` settings
*
* This is useful for workspaces that are known to not comply with catalog
* usage
*
* This filter is applied after `includedWorkspaces` and `ignoredWorkspaces`
* are resolved and takes precedence over `validation` settings
*/
noValidationWorkspaces?: string[];
/**
* Validation level for catalog usage
* - 'warn': Show warnings when catalog versions are not used (default)
Expand Down
4 changes: 4 additions & 0 deletions sources/utils/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ export async function validateCatalogUsability(
return null;
}

if (await configReader.shouldSkipWorkspaceValidation(workspace)) {
return null;
}

const defaultAliasGroups = await getDefaultAliasGroups(workspace);

// Find all groups that can access this package
Expand Down