@clearlydev/nx-archweave 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +265 -2
- package/dist/bin/review-verdict.js +330 -0
- package/dist/eslint/fsd-boundaries.d.ts +119 -0
- package/dist/eslint/fsd-boundaries.js +144 -0
- package/dist/eslint/index.d.ts +78 -0
- package/dist/eslint/index.js +256 -0
- package/dist/eslint/tsconfig-aliases.d.ts +18 -0
- package/dist/eslint/tsconfig-aliases.js +114 -0
- package/dist/generators/add-agent-rules/generator.js +550 -0
- package/dist/generators/add-agent-rules/render.js +46 -0
- package/dist/generators/add-backend/generator.js +784 -0
- package/dist/generators/add-backend/render.js +102 -0
- package/dist/generators/add-fsd-lite/generator.js +553 -0
- package/dist/generators/add-fsd-lite/render.js +72 -0
- package/dist/generators/add-github-templates/files/en/codeowners/.github/CODEOWNERS.template +4 -0
- package/dist/generators/add-github-templates/files/en/issue/.github/ISSUE_TEMPLATE/bug-report.yml.template +28 -0
- package/dist/generators/add-github-templates/files/en/issue/.github/ISSUE_TEMPLATE/feature-request.yml.template +17 -0
- package/dist/generators/add-github-templates/files/en/pr/.github/PULL_REQUEST_TEMPLATE.md.template +12 -0
- package/dist/generators/add-github-templates/files/ko/codeowners/.github/CODEOWNERS.template +4 -0
- package/dist/generators/add-github-templates/files/ko/issue/.github/ISSUE_TEMPLATE/bug-report.yml.template +28 -0
- package/dist/generators/add-github-templates/files/ko/issue/.github/ISSUE_TEMPLATE/feature-request.yml.template +17 -0
- package/dist/generators/add-github-templates/files/ko/pr/.github/PULL_REQUEST_TEMPLATE.md.template +12 -0
- package/dist/generators/add-github-templates/generator.js +125 -0
- package/dist/generators/add-github-templates/render.js +26 -0
- package/dist/generators/add-integration/generator.js +818 -0
- package/dist/generators/add-integration/render.js +34 -0
- package/dist/generators/add-review/generator.js +921 -0
- package/dist/generators/add-review/render.js +96 -0
- package/dist/generators/allow-builds/generator.js +436 -0
- package/dist/generators/allow-builds/render.js +82 -0
- package/dist/generators/doctor/generator.js +2431 -0
- package/dist/generators/doctor/integration-report.js +70 -0
- package/dist/generators/doctor/render.js +10 -0
- package/dist/generators/entity/generator.js +395 -0
- package/dist/generators/feature/generator.js +395 -0
- package/dist/generators/init/generator.js +3211 -0
- package/dist/generators/init/render.js +125 -0
- package/dist/generators/init/validation.js +74 -0
- package/dist/generators/install-integration/generator.js +1233 -0
- package/dist/generators/install-integration/render.js +128 -0
- package/dist/generators/page/generator.js +395 -0
- package/dist/generators/record-integration/generator.js +852 -0
- package/dist/generators/record-integration/render.js +31 -0
- package/dist/generators/shared/dry-run.js +12 -0
- package/dist/generators/shared/frontend-app.js +57 -0
- package/dist/generators/shared/generator.js +395 -0
- package/dist/generators/shared/project-type.js +39 -0
- package/dist/generators/shared/record-pipeline.js +34 -0
- package/dist/generators/shared/run-nx.js +24 -0
- package/dist/generators/shared/template-files.js +38 -0
- package/dist/generators/shared/toolkit-config-file.js +110 -0
- package/dist/generators/slice/core.js +185 -0
- package/dist/generators/slice/files/index/index.ts.template +14 -0
- package/dist/generators/slice/files/module/__name__.spec.ts.template +11 -0
- package/dist/generators/slice/files/module/__name__.ts.template +11 -0
- package/dist/generators/slice/files/segment/index.ts.template +20 -0
- package/dist/generators/slice/render.js +70 -0
- package/dist/generators/widget/generator.js +395 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +680 -0
- package/dist/lib/nx-plugin.d.ts +2 -0
- package/dist/lib/nx-plugin.js +7 -0
- package/dist/runtime/integrations-composition-root.js +20 -0
- package/dist/runtime/node-command-runner.js +81 -0
- package/generators.json +85 -0
- package/package.json +95 -4
- package/src/generators/add-agent-rules/schema.json +43 -0
- package/src/generators/add-backend/schema.json +39 -0
- package/src/generators/add-fsd-lite/schema.json +43 -0
- package/src/generators/add-github-templates/schema.json +35 -0
- package/src/generators/add-integration/schema.json +35 -0
- package/src/generators/add-review/schema.json +72 -0
- package/src/generators/allow-builds/schema.json +20 -0
- package/src/generators/doctor/schema.json +20 -0
- package/src/generators/entity/schema.json +37 -0
- package/src/generators/feature/schema.json +37 -0
- package/src/generators/init/schema.json +98 -0
- package/src/generators/install-integration/schema.json +35 -0
- package/src/generators/page/schema.json +53 -0
- package/src/generators/record-integration/schema.json +26 -0
- package/src/generators/shared/schema.json +32 -0
- package/src/generators/widget/schema.json +45 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/schema",
|
|
3
|
+
"cli": "nx",
|
|
4
|
+
"id": "Feature",
|
|
5
|
+
"title": "Scaffold a new FSD feature slice",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"name": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "Slice name in kebab-case (e.g. \"add-to-cart\") — becomes <app>/src/features/<name>. Invalid values are refused with a report, not thrown"
|
|
11
|
+
},
|
|
12
|
+
"publicApiName": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"description": "Exported stub function name. Defaults to the camelCase form of name"
|
|
15
|
+
},
|
|
16
|
+
"segments": {
|
|
17
|
+
"type": "array",
|
|
18
|
+
"items": { "type": "string" },
|
|
19
|
+
"description": "Extra segments to start the slice with, each as <segment>/index.ts (e.g. ui,api). model always exists — it holds the stub. Names are by purpose: ui, api, lib, config … (components/hooks/utils/types are refused)"
|
|
20
|
+
},
|
|
21
|
+
"project": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"description": "The frontend app whose source root holds the FSD layers (run add-fsd-lite first). Omitted: the workspace's one frontend application; with more than one, the run is refused with the candidates listed"
|
|
24
|
+
},
|
|
25
|
+
"locale": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"enum": ["ko", "en"],
|
|
28
|
+
"description": "Generated content language. Defaults to toolkit.config.json's locale, else en"
|
|
29
|
+
},
|
|
30
|
+
"dryRun": {
|
|
31
|
+
"type": "boolean",
|
|
32
|
+
"default": false,
|
|
33
|
+
"description": "Print the files that would be generated without writing anything"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"required": ["name"]
|
|
37
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/schema",
|
|
3
|
+
"cli": "nx",
|
|
4
|
+
"id": "Init",
|
|
5
|
+
"title": "Run the preset-driven init pipeline (FSD Lite, agent rules, GitHub templates, review)",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"preset": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"enum": [
|
|
11
|
+
"quick-start",
|
|
12
|
+
"solo",
|
|
13
|
+
"team"
|
|
14
|
+
],
|
|
15
|
+
"description": "Preset that determines each sub-generator's default configuration (buildPresetConfig). Recorded in toolkit.config.json; omitted: the recorded one (required when none is recorded)"
|
|
16
|
+
},
|
|
17
|
+
"frontend": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"enum": [
|
|
20
|
+
"react-vite",
|
|
21
|
+
"next",
|
|
22
|
+
"none"
|
|
23
|
+
],
|
|
24
|
+
"description": "Target frontend; 'none' is a backend-only workspace (needs --backend; FSD is skipped). Recorded in toolkit.config.json; omitted: the recorded one (required when none is recorded)"
|
|
25
|
+
},
|
|
26
|
+
"backend": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"enum": [
|
|
29
|
+
"express",
|
|
30
|
+
"nest",
|
|
31
|
+
"none"
|
|
32
|
+
],
|
|
33
|
+
"description": "Backend app wiring: delegates app creation to the official @nx generators and wires shared-types, scope tags, and module-boundary constraints. 'none' leaves the workspace frontend-only. Omitted: toolkit.config.json's backend (add-backend records it), else none — no schema default, which would shadow the recorded one"
|
|
34
|
+
},
|
|
35
|
+
"locale": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"enum": [
|
|
38
|
+
"ko",
|
|
39
|
+
"en"
|
|
40
|
+
],
|
|
41
|
+
"description": "Generated content language, passed through to every enabled sub-generator and recorded in toolkit.config.json. Omitted: the recorded locale, else en (no schema default — Nx would apply it even when the flag is omitted and shadow the recorded value)"
|
|
42
|
+
},
|
|
43
|
+
"reviewProvider": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"enum": ["claude", "gemini", "gpt", "cursor"],
|
|
46
|
+
"description": "Review provider when review is on. Omitted: toolkit.config.json's review.provider (unless --aiHosts changes the first host), else the first AI host's (Claude Code → claude, Codex → gpt, Cursor → cursor, Gemini CLI → gemini)"
|
|
47
|
+
},
|
|
48
|
+
"aiHosts": {
|
|
49
|
+
"type": "array",
|
|
50
|
+
"items": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"enum": ["claude-code", "codex", "cursor", "gemini-cli"]
|
|
53
|
+
},
|
|
54
|
+
"description": "The AI coding tools the workspace is set up for: they decide the agent-rules files (Claude Code → CLAUDE.md; Codex, Cursor, Gemini CLI → AGENTS.md) and the review provider default (the first one's). Recorded in toolkit.config.json. Default: Claude Code."
|
|
55
|
+
},
|
|
56
|
+
"fsd": {
|
|
57
|
+
"type": "boolean",
|
|
58
|
+
"description": "Toggle add-fsd-lite. Defaults to the preset's enablement (always on)"
|
|
59
|
+
},
|
|
60
|
+
"agentRules": {
|
|
61
|
+
"type": "boolean",
|
|
62
|
+
"description": "Toggle add-agent-rules. Defaults to the preset's enablement (on)"
|
|
63
|
+
},
|
|
64
|
+
"githubTemplates": {
|
|
65
|
+
"type": "boolean",
|
|
66
|
+
"description": "Toggle add-github-templates. Defaults to the preset's enablement (on)"
|
|
67
|
+
},
|
|
68
|
+
"review": {
|
|
69
|
+
"type": "boolean",
|
|
70
|
+
"description": "Toggle add-review. Defaults to the preset's review.enabled default (quick-start: off, solo/team: on)"
|
|
71
|
+
},
|
|
72
|
+
"dryRun": {
|
|
73
|
+
"type": "boolean",
|
|
74
|
+
"default": false,
|
|
75
|
+
"description": "Preview the full pipeline: zero writes, and the post-run validation callback never runs"
|
|
76
|
+
},
|
|
77
|
+
"allowDirty": {
|
|
78
|
+
"type": "boolean",
|
|
79
|
+
"default": false,
|
|
80
|
+
"description": "Run even when the working tree has uncommitted changes. The report then warns that `git restore .` also reverts those pre-existing changes. Used by create-nx-archweave, whose own plugin-install step dirties the fresh workspace right before init runs"
|
|
81
|
+
},
|
|
82
|
+
"applyEslint": {
|
|
83
|
+
"type": "boolean",
|
|
84
|
+
"default": false,
|
|
85
|
+
"description": "Write the ESLint rules for real — the scope depConstraints into the root config (add-backend) and the archweave/fsd-boundaries rule into the frontend app's config (add-fsd-lite) — instead of printing report-only snippets. The create CLI passes this on workspaces it just created; leave it off to keep existing configs untouched"
|
|
86
|
+
},
|
|
87
|
+
"packageManager": {
|
|
88
|
+
"type": "string",
|
|
89
|
+
"enum": [
|
|
90
|
+
"pnpm",
|
|
91
|
+
"npm",
|
|
92
|
+
"yarn",
|
|
93
|
+
"bun"
|
|
94
|
+
],
|
|
95
|
+
"description": "Package manager of the workspace (default: detected from lockfiles, then the one recorded in toolkit.config.json, then pnpm). Recorded in toolkit.config.json."
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/schema",
|
|
3
|
+
"cli": "nx",
|
|
4
|
+
"id": "InstallIntegration",
|
|
5
|
+
"title": "Install a supported external integration through its official CLI, verify it live, record and declare it",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"tool": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"enum": ["ponytail"],
|
|
11
|
+
"description": "The plugin to install, by its plugin-catalog id (@nx-archweave/integrations plugin-catalog.ts). The enum is kept equal to the catalog by a test."
|
|
12
|
+
},
|
|
13
|
+
"target": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"enum": ["claude-code"],
|
|
16
|
+
"default": "claude-code",
|
|
17
|
+
"description": "The host environment to install into. Hosts with a plugin adapter; today only 'claude-code'."
|
|
18
|
+
},
|
|
19
|
+
"scope": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"enum": ["user", "project", "local"],
|
|
22
|
+
"description": "Claude Code plugin scope for `claude plugin install --scope` (default project, which records it in the workspace's .claude/settings.json; local writes its personal .claude/settings.local.json; user installs for every project of this user). When given explicitly, an existing install at a different scope is reported instead of counted as installed."
|
|
23
|
+
},
|
|
24
|
+
"versionRange": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"description": "Version to show as requested. Not enforced — the marketplace decides the plugin version, and the version actually installed is the one recorded."
|
|
27
|
+
},
|
|
28
|
+
"apply": {
|
|
29
|
+
"type": "boolean",
|
|
30
|
+
"default": false,
|
|
31
|
+
"description": "Run the official install command, re-verify live, record the manifest entry and declare the integration in toolkit.config.json. Without it (default) the generator only checks the current state and shows the exact command. Ignored when Nx's own --dry-run is set."
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"required": ["tool"]
|
|
35
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/schema",
|
|
3
|
+
"cli": "nx",
|
|
4
|
+
"id": "Page",
|
|
5
|
+
"title": "Scaffold a new FSD page slice",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"name": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "Slice name in kebab-case (e.g. \"home\") — becomes <app>/src/pages/<name> (a screen composed from the layers below). Invalid values are refused with a report, not thrown"
|
|
11
|
+
},
|
|
12
|
+
"publicApiName": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"description": "Exported stub function name. Defaults to the camelCase form of name"
|
|
15
|
+
},
|
|
16
|
+
"segments": {
|
|
17
|
+
"type": "array",
|
|
18
|
+
"items": {
|
|
19
|
+
"type": "string"
|
|
20
|
+
},
|
|
21
|
+
"description": "Extra segments to start the slice with, each as <segment>/index.ts (e.g. ui,api). model always exists — it holds the stub. Names are by purpose: ui, api, lib, config … (components/hooks/utils/types are refused)"
|
|
22
|
+
},
|
|
23
|
+
"project": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"description": "The frontend app whose source root holds the FSD layers. Omitted: the workspace's one frontend application; with more than one, the run is refused with the candidates listed"
|
|
26
|
+
},
|
|
27
|
+
"frontend": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"enum": [
|
|
30
|
+
"react-vite",
|
|
31
|
+
"next"
|
|
32
|
+
],
|
|
33
|
+
"description": "Decides the layer directory: pages/ on react-vite, _pages/ on next (Next.js reads src/pages as its Pages Router). Omitted: toolkit.config.json's frontend (init records it), else detected from a next.config file in the app, else react-vite"
|
|
34
|
+
},
|
|
35
|
+
"locale": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"enum": [
|
|
38
|
+
"ko",
|
|
39
|
+
"en"
|
|
40
|
+
],
|
|
41
|
+
"description": "Generated content language. Defaults to toolkit.config.json's locale, else en"
|
|
42
|
+
},
|
|
43
|
+
"dryRun": {
|
|
44
|
+
"type": "boolean",
|
|
45
|
+
"default": false,
|
|
46
|
+
"description": "Print the files that would be generated without writing anything"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"required": [
|
|
50
|
+
"name"
|
|
51
|
+
],
|
|
52
|
+
"description": "Scaffold a new FSD page slice. On Next.js the layer directory is _pages (Next.js reads src/pages as its Pages Router)"
|
|
53
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/schema",
|
|
3
|
+
"cli": "nx",
|
|
4
|
+
"id": "RecordIntegration",
|
|
5
|
+
"title": "Verify a manually installed integration and record its verified state",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"tool": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"enum": ["ponytail"],
|
|
11
|
+
"description": "The plugin to verify and record, by its plugin-catalog id (@nx-archweave/integrations plugin-catalog.ts). The enum is kept equal to the catalog by a test."
|
|
12
|
+
},
|
|
13
|
+
"target": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"enum": ["claude-code"],
|
|
16
|
+
"default": "claude-code",
|
|
17
|
+
"description": "The host environment to verify against. Hosts with a plugin adapter; today only 'claude-code'."
|
|
18
|
+
},
|
|
19
|
+
"apply": {
|
|
20
|
+
"type": "boolean",
|
|
21
|
+
"default": false,
|
|
22
|
+
"description": "Write the manifest entry (only if live verification succeeds). Without it (default) the generator performs the same live read-only verification and shows what would be recorded, writing nothing. Ignored when Nx's own --dry-run is set."
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"required": ["tool"]
|
|
26
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/schema",
|
|
3
|
+
"cli": "nx",
|
|
4
|
+
"id": "Shared",
|
|
5
|
+
"title": "Scaffold a new FSD shared slice",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"name": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "Slice name in kebab-case (e.g. \"date-format\") — becomes <app>/src/shared/<name>. Invalid values are refused with a report, not thrown"
|
|
11
|
+
},
|
|
12
|
+
"publicApiName": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"description": "Exported stub function name. Defaults to the camelCase form of name"
|
|
15
|
+
},
|
|
16
|
+
"project": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"description": "The frontend app whose source root holds the FSD layers (run add-fsd-lite first). Omitted: the workspace's one frontend application; with more than one, the run is refused with the candidates listed"
|
|
19
|
+
},
|
|
20
|
+
"locale": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"enum": ["ko", "en"],
|
|
23
|
+
"description": "Generated content language. Defaults to toolkit.config.json's locale, else en"
|
|
24
|
+
},
|
|
25
|
+
"dryRun": {
|
|
26
|
+
"type": "boolean",
|
|
27
|
+
"default": false,
|
|
28
|
+
"description": "Print the files that would be generated without writing anything"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"required": ["name"]
|
|
32
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/schema",
|
|
3
|
+
"cli": "nx",
|
|
4
|
+
"id": "Widget",
|
|
5
|
+
"title": "Scaffold a new FSD widget slice",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"name": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "Slice name in kebab-case (e.g. \"header\") — becomes <app>/src/widgets/<name> (a self-contained block of UI composed from features and entities). Invalid values are refused with a report, not thrown"
|
|
11
|
+
},
|
|
12
|
+
"publicApiName": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"description": "Exported stub function name. Defaults to the camelCase form of name"
|
|
15
|
+
},
|
|
16
|
+
"segments": {
|
|
17
|
+
"type": "array",
|
|
18
|
+
"items": {
|
|
19
|
+
"type": "string"
|
|
20
|
+
},
|
|
21
|
+
"description": "Extra segments to start the slice with, each as <segment>/index.ts (e.g. ui,api). model always exists — it holds the stub. Names are by purpose: ui, api, lib, config … (components/hooks/utils/types are refused)"
|
|
22
|
+
},
|
|
23
|
+
"project": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"description": "The frontend app whose source root holds the FSD layers. Omitted: the workspace's one frontend application; with more than one, the run is refused with the candidates listed"
|
|
26
|
+
},
|
|
27
|
+
"locale": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"enum": [
|
|
30
|
+
"ko",
|
|
31
|
+
"en"
|
|
32
|
+
],
|
|
33
|
+
"description": "Generated content language. Defaults to toolkit.config.json's locale, else en"
|
|
34
|
+
},
|
|
35
|
+
"dryRun": {
|
|
36
|
+
"type": "boolean",
|
|
37
|
+
"default": false,
|
|
38
|
+
"description": "Print the files that would be generated without writing anything"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"required": [
|
|
42
|
+
"name"
|
|
43
|
+
],
|
|
44
|
+
"description": "Scaffold a new FSD widget slice. widgets is outside the Lite default scope — this generator is how a workspace opts in; the lint rule already knows the layer"
|
|
45
|
+
}
|