@sidebase/base-config 0.3.2 → 0.3.4

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/README.md CHANGED
@@ -23,9 +23,9 @@ instead.
23
23
 
24
24
  | File | Managed as | What it is |
25
25
  | ---- | ---------- | ---------- |
26
- | `.github/workflows/ci.yml` | owned | Lint, typecheck and build, plus optional test and e2e jobs |
27
- | `.github/workflows/pr-preview-cleanup.yml` | owned | Removes the `preview-deployed` label when `preview` is removed |
28
- | `.github/workflows/streamctl-upgrade.yml` | owned, opt-in | Weekly upgrade PRs (see [Upgrades](#upgrades)) |
26
+ | `.github/workflows/ci.yaml` | owned | Lint, typecheck and build, plus optional test and e2e jobs |
27
+ | `.github/workflows/pr-preview-cleanup.yaml` | owned | Removes the `preview-deployed` label when `preview` is removed |
28
+ | `.github/workflows/streamctl-upgrade.yaml` | owned, opt-in | Weekly upgrade PRs (see [Upgrades](#upgrades)) |
29
29
  | `Dockerfile` | owned | Production image, runs `prisma migrate deploy` and then the server |
30
30
  | `tsconfig.json` | owned | Extends the shared strict base and Nuxt's generated config |
31
31
  | `pnpm-workspace.yaml` | owned | 7-day release cooldown and the install-script allowlist |
@@ -176,7 +176,7 @@ TypeScript config file) in your devDependencies; `sync` adds both.
176
176
  | `trpcGuard` | `false` | Ban `publicProcedure` in `server/trpc/routers/**` |
177
177
  | `prismaImportGuard` | `false` | Ban `~~/prisma/` client imports outside server code |
178
178
  | `typeDefStyle` | `"interface"` | Prefer `interface` or `type` |
179
- | `autoImportPaths` | `["utils/", "composables/", "~~/shared/types/"]` | Paths Nuxt auto-imports, so direct imports are banned |
179
+ | `autoImportPaths` | `["utils/", "composables/", "~~/shared/types/"]` | Paths Nuxt auto-imports, so direct imports are banned in app code. The defaults cover `app/utils`, `shared/utils`, `app/composables` and `shared/types` under every Nuxt alias. Files in the root (or `layers/*`) `server/` and `shared/` dirs and relative imports are never matched |
180
180
  | `autoImportTypeOnly` | `[]` | Of those, paths where `import type` stays allowed |
181
181
  | `ignoresTypeAware` | config files | Files excluded from type-aware linting |
182
182
  | `testFilePattern` | `*.test`, `*.spec`, `*.stories` | Files where the `process.env` and auto-import bans are off |
package/dist/config.d.mts CHANGED
@@ -25,7 +25,7 @@ interface NuxtBaseCiConfig {
25
25
  aptPackages?: string[];
26
26
  }
27
27
  /**
28
- * Runtime version pins (`config.versions.*`). `node` feeds every render (`ci.yml`,
28
+ * Runtime version pins (`config.versions.*`). `node` feeds every render (`ci.yaml`,
29
29
  * the Dockerfile, the upgrade workflow); `pnpm` feeds the Dockerfile only, because CI
30
30
  * takes pnpm from `package.json#packageManager`. Defaults come from the version
31
31
  * baseline (node 24.21.0, pnpm 10.34.5).
package/dist/config.d.ts CHANGED
@@ -25,7 +25,7 @@ interface NuxtBaseCiConfig {
25
25
  aptPackages?: string[];
26
26
  }
27
27
  /**
28
- * Runtime version pins (`config.versions.*`). `node` feeds every render (`ci.yml`,
28
+ * Runtime version pins (`config.versions.*`). `node` feeds every render (`ci.yaml`,
29
29
  * the Dockerfile, the upgrade workflow); `pnpm` feeds the Dockerfile only, because CI
30
30
  * takes pnpm from `package.json#packageManager`. Defaults come from the version
31
31
  * baseline (node 24.21.0, pnpm 10.34.5).
@@ -12,7 +12,12 @@ interface CreateSidebaseEslintOptions {
12
12
  prismaImportGuard?: boolean;
13
13
  /** `ts/consistent-type-definitions`. Default `"interface"`. */
14
14
  typeDefStyle?: "interface" | "type";
15
- /** Paths banned from direct import (Nuxt auto-import). Default `["utils/", "composables/", "~~/shared/types/"]`. */
15
+ /**
16
+ * Paths banned from direct import (Nuxt auto-import). Default `["utils/", "composables/", "~~/shared/types/"]`.
17
+ * The defaults expand to the Nuxt 4 app-side dirs (`app/` and `shared/` utils) under every
18
+ * default alias. Paths starting with `~`, `@`, `#`, `/`, `./` or `../` are used as-is.
19
+ * Not applied to files in the root (or `layers/*`) `server/` and `shared/` dirs.
20
+ */
16
21
  autoImportPaths?: string[];
17
22
  /**
18
23
  * Subset of {@link autoImportPaths} where `import type { ... }` is still allowed
@@ -12,7 +12,12 @@ interface CreateSidebaseEslintOptions {
12
12
  prismaImportGuard?: boolean;
13
13
  /** `ts/consistent-type-definitions`. Default `"interface"`. */
14
14
  typeDefStyle?: "interface" | "type";
15
- /** Paths banned from direct import (Nuxt auto-import). Default `["utils/", "composables/", "~~/shared/types/"]`. */
15
+ /**
16
+ * Paths banned from direct import (Nuxt auto-import). Default `["utils/", "composables/", "~~/shared/types/"]`.
17
+ * The defaults expand to the Nuxt 4 app-side dirs (`app/` and `shared/` utils) under every
18
+ * default alias. Paths starting with `~`, `@`, `#`, `/`, `./` or `../` are used as-is.
19
+ * Not applied to files in the root (or `layers/*`) `server/` and `shared/` dirs.
20
+ */
16
21
  autoImportPaths?: string[];
17
22
  /**
18
23
  * Subset of {@link autoImportPaths} where `import type { ... }` is still allowed
@@ -35,6 +35,8 @@ const TRPC_ROUTERS_GLOB = "**/server/trpc/routers/**/*.{ts,js}";
35
35
  const ZOD_DEPRECATED_METHODS = ["extend", "merge", "passthrough"];
36
36
  const APP_FILES = ["**/*.{ts,vue}"];
37
37
  const APP_IGNORES = ["**/*.d.ts"];
38
+ const SHARED_FILES = ["shared/**/*.{ts,vue}", "layers/*/shared/**/*.{ts,vue}"];
39
+ const SERVER_FILES = ["server/**/*.{ts,vue}", "layers/*/server/**/*.{ts,vue}"];
38
40
  const PROCESS_ENV_PROPERTY = {
39
41
  object: "process",
40
42
  property: "env",
@@ -71,12 +73,26 @@ function zodImportStyleBans(options) {
71
73
  { selector: `ExportNamedDeclaration[source.value=${ZOD_SOURCE}][exportKind!='type'] ExportSpecifier[local.name='z'][exportKind!='type']`, message: ZOD_IMPORT_STYLE_MESSAGE }
72
74
  ];
73
75
  }
76
+ const NUXT_AUTO_IMPORT_GLOBS = /* @__PURE__ */ new Map([
77
+ ["utils", [
78
+ "~/utils/**",
79
+ "@/utils/**",
80
+ "~~/app/utils/**",
81
+ "@@/app/utils/**",
82
+ "~~/shared/utils/**",
83
+ "@@/shared/utils/**",
84
+ "#shared/utils/**"
85
+ ]],
86
+ ["composables", ["~/composables/**", "@/composables/**", "~~/app/composables/**", "@@/app/composables/**"]],
87
+ ["~~/shared/types", ["~~/shared/types/**", "@@/shared/types/**", "#shared/types/**"]]
88
+ ]);
89
+ function escapeGlob(glob) {
90
+ return glob.replace(/^#/, "\\#");
91
+ }
74
92
  function anchoredGlobs(rawPath) {
75
93
  const path = rawPath.replace(/\/+$/, "");
76
- if (/^(?:[~@#/]|\.{1,2}\/)/.test(path)) {
77
- return [`${path}/**`];
78
- }
79
- return [`~/${path}/**`, `~~/${path}/**`, `@/${path}/**`];
94
+ const [first, ...rest] = NUXT_AUTO_IMPORT_GLOBS.get(path) ?? (/^(?:[~@#/]|\.{1,2}\/)/.test(path) ? [`${path}/**`] : [`~/${path}/**`, `~~/${path}/**`, `@/${path}/**`]);
95
+ return [escapeGlob(first), ...rest.map(escapeGlob)];
80
96
  }
81
97
  function importRestrictions(options) {
82
98
  const paths = [
@@ -150,18 +166,27 @@ function buildEslintLayers(options) {
150
166
  }
151
167
  });
152
168
  const { paths, patterns } = importRestrictions(options);
153
- const appRules = {
154
- "no-restricted-properties": ["error", PROCESS_ENV_PROPERTY]
155
- };
156
- if (paths.length > 0 || patterns.length > 0) {
157
- appRules["ts/no-restricted-imports"] = ["error", { paths, patterns }];
169
+ const prismaPatterns = options.prismaImportGuard ? [{
170
+ group: ["~~/prisma/*", "~~/prisma/**"],
171
+ message: "Do not import the Prisma client on the app side. Use a server-only module."
172
+ }] : [];
173
+ const scopes = [
174
+ ["sidebase/restricted-app", APP_FILES, [...patterns, ...prismaPatterns]],
175
+ ["sidebase/restricted-shared", SHARED_FILES, prismaPatterns],
176
+ // Server code legitimately imports the Prisma client.
177
+ ["sidebase/restricted-server", SERVER_FILES, []]
178
+ ];
179
+ for (const [name, files, scopePatterns] of scopes) {
180
+ configs.push({
181
+ name,
182
+ files,
183
+ ignores: APP_IGNORES,
184
+ rules: {
185
+ "no-restricted-properties": ["error", PROCESS_ENV_PROPERTY],
186
+ "ts/no-restricted-imports": ["error", scopePatterns.length > 0 ? { paths, patterns: scopePatterns } : { paths }]
187
+ }
188
+ });
158
189
  }
159
- configs.push({
160
- name: "sidebase/restricted-app",
161
- files: APP_FILES,
162
- ignores: APP_IGNORES,
163
- rules: appRules
164
- });
165
190
  const zodSyntax = [...zodImportStyleBans(options), ...zodMethodBans(options)];
166
191
  if (zodSyntax.length > 0) {
167
192
  configs.push({
@@ -180,18 +205,6 @@ function buildEslintLayers(options) {
180
205
  rules: { "no-restricted-syntax": ["error", ...zodSyntax, PUBLIC_PROCEDURE_SELECTOR] }
181
206
  });
182
207
  }
183
- if (options.prismaImportGuard) {
184
- const prismaPattern = {
185
- group: ["~~/prisma/*", "~~/prisma/**"],
186
- message: "Do not import the Prisma client on the app side. Use a server-only module."
187
- };
188
- configs.push({
189
- name: "sidebase/prisma-import-guard",
190
- files: APP_FILES,
191
- ignores: [...APP_IGNORES, "**/server/**"],
192
- rules: { "ts/no-restricted-imports": ["error", { paths, patterns: [...patterns, prismaPattern] }] }
193
- });
194
- }
195
208
  if (options.testFilePattern.length > 0) {
196
209
  configs.push({
197
210
  name: "sidebase/test-overrides",
package/docs/migration.md CHANGED
@@ -607,38 +607,40 @@ anything it managed under an old path or an old payload is simply dropped from t
607
607
  managed set rather than removed. `check` reports clean either way, because it only
608
608
  inspects paths that are currently managed. Nothing will ever tell you these are there.
609
609
 
610
- #### 9a. The two orphaned workflow files
610
+ #### 9a. The orphaned `.yml` workflow files
611
611
 
612
- Do this only if your repository synced the payload before the workflow extensions were
613
- normalized, which is the case for every adoption branch cut before this release. Check:
612
+ Do this only if your repository synced a payload that still shipped `.yml` workflows,
613
+ which is the case for every adoption branch cut before this release. Check:
614
614
 
615
615
  ```sh
616
616
  ls .github/workflows/
617
617
  ```
618
618
 
619
- If you see `ci.yaml` or `pr-preview-cleanup.yaml` sitting next to `ci.yml` and
620
- `pr-preview-cleanup.yml`, remove the `.yaml` pair:
619
+ If you see `ci.yml`, `pr-preview-cleanup.yml` or `streamctl-upgrade.yml` sitting next to
620
+ its `.yaml` counterpart, remove the `.yml` copies:
621
621
 
622
622
  ```sh
623
- git rm --ignore-unmatch .github/workflows/ci.yaml .github/workflows/pr-preview-cleanup.yaml
624
- ```
625
-
626
- `--ignore-unmatch` matters: the condition above is "either file", but `git rm` is atomic, so
627
- naming a path that does not exist aborts with `fatal: pathspec ... did not match any files`
628
- and removes NEITHER. A repository that opted one workflow out, or never enabled preview
629
- cleanup, would skim that error and keep running both copies of the other.
630
-
631
- This is required, not tidying. The payload renamed those two managed files from `.yaml` to
632
- `.yml`. streamctl manages files by path, so the sync in step 7 wrote the new `.yml` files
633
- but did NOT delete the old `.yaml` ones: they are simply no longer in the managed set.
634
- GitHub Actions runs every file in `.github/workflows/`, so if you leave them the
635
- repository runs BOTH copies. That means duplicate CI on every push, and two
636
- `pr-preview-cleanup` jobs racing to tear down the same preview environment. The leftover
637
- copies are also frozen forever, so they never receive later fixes, including security
638
- updates to pinned action SHAs.
639
-
640
- While you are here, if your config opts out of either workflow by its old path, for
641
- example `files: { ".github/workflows/ci.yaml": "off" }`, re-key it to the `.yml` path.
623
+ git rm --ignore-unmatch .github/workflows/ci.yml .github/workflows/pr-preview-cleanup.yml .github/workflows/streamctl-upgrade.yml
624
+ ```
625
+
626
+ `--ignore-unmatch` matters: the condition above is "any of these files", but `git rm` is
627
+ atomic, so naming a path that does not exist aborts with `fatal: pathspec ... did not match
628
+ any files` and removes NONE. A repository that opted one workflow out, or never enabled
629
+ preview cleanup or upgrade PRs, would skim that error and keep running both copies of the
630
+ others.
631
+
632
+ This is required, not tidying. The payload renamed those managed files from `.yml` to
633
+ `.yaml`, so every YAML file it ships uses the same extension as `pnpm-workspace.yaml`.
634
+ streamctl manages files by path, so the sync in step 7 wrote the new `.yaml` files but did
635
+ NOT delete the old `.yml` ones: they are simply no longer in the managed set. GitHub
636
+ Actions runs every file in `.github/workflows/`, so if you leave them the repository runs
637
+ BOTH copies. That means duplicate CI on every push, two `pr-preview-cleanup` jobs racing to
638
+ tear down the same preview environment, and two weekly upgrade PRs. The leftover copies
639
+ are also frozen forever, so they never receive later fixes, including security updates to
640
+ pinned action SHAs.
641
+
642
+ While you are here, if your config opts out of any of these workflows by its old path, for
643
+ example `files: { ".github/workflows/ci.yml": "off" }`, re-key it to the `.yaml` path.
642
644
  An opt-out naming a path that is no longer managed does nothing, so the file you meant to
643
645
  suppress would start arriving on the next sync.
644
646
 
@@ -646,7 +648,7 @@ suppress would start arriving on the next sync.
646
648
 
647
649
  The same problem in a harder-to-see form. A `block` file keeps its markers after the
648
650
  payload stops managing that path, so the block sits there forever: never updated, never
649
- reported, and indistinguishable from a block that is still live. The two `.yaml`
651
+ reported, and indistinguishable from a block that is still live. The `.yml`
650
652
  workflows above are the known orphaned FILES for this release; orphaned blocks are not
651
653
  enumerable in advance, because they depend on which payload version your repository
652
654
  adopted first.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sidebase/base-config",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "Shared @sidebase base configuration for Nuxt repos: ESLint / Prisma / tsconfig factories on npm, plus the streamctl file-sync preset payload",
5
5
  "keywords": [
6
6
  "sidebase",
@@ -10,7 +10,7 @@
10
10
  { "path": ".vscode/settings.json", "strategy": "merge", "source": "base/vscode/settings.json", "render": "editor", "projectFields": ["editor.fontSize", "editor.rulers", "editor.formatOnSave", "files.exclude", "search.exclude", "files.watcherExclude"] },
11
11
  { "path": ".vscode/extensions.json", "strategy": "merge", "source": "base/vscode/extensions.json", "projectFields": ["unwantedRecommendations"] },
12
12
  { "path": "AGENTS.md", "strategy": "full", "source": "base/AGENTS.md", "adoption": "expected" },
13
- { "path": ".github/workflows/streamctl-upgrade.yml", "strategy": "full", "source": "base/github/workflows/streamctl-upgrade.yml", "render": "automation-upgrade", "enabledBy": "automation.upgradePr", "adoption": "unexpected" }
13
+ { "path": ".github/workflows/streamctl-upgrade.yaml", "strategy": "full", "source": "base/github/workflows/streamctl-upgrade.yaml", "render": "automation-upgrade", "enabledBy": "automation.upgradePr", "adoption": "unexpected" }
14
14
  ],
15
15
  "renders": {
16
16
  "automation-upgrade": {
@@ -32,7 +32,7 @@
32
32
  "MIN_RELEASE_AGE": { "configPath": "security.minimumReleaseAge", "default": "10080", "pattern": "^\\d+$" }
33
33
  },
34
34
  "fragments": [
35
- { "forEach": "pnpm.onlyBuiltDependencies", "source": "base/templates/pnpm/only-built-dependency.yml" }
35
+ { "forEach": "pnpm.onlyBuiltDependencies", "source": "base/templates/pnpm/only-built-dependency.yaml" }
36
36
  ]
37
37
  }
38
38
  },
@@ -6,8 +6,8 @@
6
6
  { "path": "eslint.config.ts", "strategy": "scaffold", "source": "nuxt-app/eslint.config.ts", "shadowedBy": ["eslint.config.mjs", "eslint.config.js", "eslint.config.cjs"] },
7
7
  { "path": "prisma.config.ts", "strategy": "scaffold", "source": "nuxt-app/prisma.config.ts" },
8
8
  { "path": "Dockerfile", "strategy": "full", "source": "nuxt-app/Dockerfile", "render": "dockerfile", "adoption": "expected" },
9
- { "path": ".github/workflows/pr-preview-cleanup.yml", "strategy": "full", "source": "nuxt-app/github/workflows/pr-preview-cleanup.yml", "adoption": "expected" },
10
- { "path": ".github/workflows/ci.yml", "strategy": "full", "source": "nuxt-app/github/workflows/ci.yml", "render": "ci" }
9
+ { "path": ".github/workflows/pr-preview-cleanup.yaml", "strategy": "full", "source": "nuxt-app/github/workflows/pr-preview-cleanup.yaml", "adoption": "expected" },
10
+ { "path": ".github/workflows/ci.yaml", "strategy": "full", "source": "nuxt-app/github/workflows/ci.yaml", "render": "ci" }
11
11
  ],
12
12
  "renders": {
13
13
  "ci": {
@@ -16,8 +16,8 @@
16
16
  "NODE_VERSION": { "configPath": "versions.node", "default": "24.21.0", "pattern": "^[\\w.+-]+$" }
17
17
  },
18
18
  "fragments": [
19
- { "toggle": "ci.unitTests", "source": "nuxt-app/templates/ci/test-job.yml" },
20
- { "toggle": "ci.e2e", "source": "nuxt-app/templates/ci/e2e-job.yml" }
19
+ { "toggle": "ci.unitTests", "source": "nuxt-app/templates/ci/test-job.yaml" },
20
+ { "toggle": "ci.e2e", "source": "nuxt-app/templates/ci/e2e-job.yaml" }
21
21
  ]
22
22
  },
23
23
  "dockerfile": {