@10stars/config 16.0.2 → 16.1.0

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.
@@ -17,6 +17,18 @@
17
17
  "ignore": ["sx"]
18
18
  }
19
19
  ],
20
- "react/react-in-jsx-scope": "off"
20
+ "react/react-in-jsx-scope": "off",
21
+ "no-restricted-imports": [
22
+ "error",
23
+ {
24
+ "paths": [
25
+ {
26
+ "name": "@vanilla-extract/css",
27
+ "importNames": ["style"],
28
+ "message": "Do not use 'style' from '@vanilla-extract/css'. Use 'theme.layerStyle(import.meta, ...)' instead so the rule lands in a cascade layer"
29
+ }
30
+ ]
31
+ }
32
+ ]
21
33
  }
22
34
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@10stars/config",
3
- "version": "16.0.2",
3
+ "version": "16.1.0",
4
4
  "private": false,
5
5
  "bin": {
6
6
  "config": "./src/index.ts"
@@ -26,6 +26,7 @@
26
26
  "@types/node": "^26.1.0",
27
27
  "@types/react": "^19.2.17",
28
28
  "@types/react-dom": "^19.2.3",
29
+ "@typescript/native-preview": "7.0.0-dev.20260704.1",
29
30
  "husky": "^9.1.7",
30
31
  "oxfmt": "0.57.0",
31
32
  "oxlint": "^1.72.0",
package/src/index.ts CHANGED
@@ -6,8 +6,9 @@ import path from "node:path"
6
6
  import { resetColorIndex } from "./colors"
7
7
  import { checkIsMonorepo, dedupeSingletons, linkPackages } from "./link-packages"
8
8
  import { setupOverrides } from "./manage-overrides"
9
- import { patchVanillaExtract } from "./patch-vanilla-extract"
10
9
  import { runConcurrently, type CommandConfig } from "./runner"
10
+ import { setupSideEffects } from "./vanilla-extract/manage-side-effects"
11
+ import { patchVanillaExtract } from "./vanilla-extract/patch-vanilla-extract"
11
12
  import { setupVSCode } from "./vscode-config"
12
13
 
13
14
  const cwd = process.env.PWD!
@@ -39,13 +40,11 @@ const getOxlintCommonCmd = async () => {
39
40
  return `${paths} ${quiet} --ignore-pattern '**/node_modules/**' --ignore-pattern '**/dist/**'`
40
41
  }
41
42
 
42
- const typecheckAndWatch = () => exec(`tsc --project ./tsconfig.json --noEmit --watch`)
43
-
44
43
  const actions = {
45
44
  prepare: {
46
45
  info: `Runs husky and links packages`,
47
46
  action: async () => {
48
- await patchVanillaExtract(cwd) // patch VE before the CI guard: builds (incl. CI) need the `css.ts`-only filter in node_modules
47
+ const veInstalled = await patchVanillaExtract(cwd) // patch VE before the CI guard: builds (incl. CI) need the `css.ts`-only filter in node_modules
49
48
 
50
49
  if (process.env.CI) return
51
50
 
@@ -57,6 +56,7 @@ const actions = {
57
56
  exec(`husky`) // husky is intended only for local development (not in CI)
58
57
  await setupVSCode(cwd) // create/update .vscode directory with standardized extensions.json and settings.json
59
58
  await setupOverrides(cwd) // sync standardized package.json overrides
59
+ await setupSideEffects(cwd, veInstalled) // CSS-protect VE repos; `sideEffects: false` for non-VE libs
60
60
 
61
61
  const config = await getConfig()
62
62
  if (config?.linkPackages) {
@@ -70,13 +70,9 @@ const actions = {
70
70
  }
71
71
  },
72
72
  },
73
- typecheckWatch: {
74
- info: "Typecheck code (in watch mode)",
75
- action: typecheckAndWatch,
76
- },
77
73
  ts: {
78
- info: `alias for 'typecheckWatch'`,
79
- action: typecheckAndWatch,
74
+ info: `Typecheck code (in watch mode)`,
75
+ action: () => exec(`tsgo --project ./tsconfig.json --noEmit --watch`), // watch uses tsgo (native-preview) for the FSEvents watcher fix absent from typescript@7.0.1-rc; non-watch typecheck stays on tsc
80
76
  },
81
77
  typecheck: {
82
78
  info: `Typecheck code (no watch)`,
@@ -0,0 +1,63 @@
1
+ import { execSync } from "node:child_process"
2
+ import fs from "node:fs/promises"
3
+ import path from "node:path"
4
+
5
+ import type * as TF from "type-fest"
6
+
7
+ // VE `css.ts` modules and raw `.css` files inject styles purely as a side effect — a global reset,
8
+ // theme-var override or `@font-face` is imported for that effect alone (no used exports). A
9
+ // `sideEffects: false` package tree-shakes such a bare import away, shipping an unstyled build (and,
10
+ // on SSR, a hydration mismatch — React #418 — since the server-rendered markup no longer matches).
11
+ // Listing them keeps them alive. `css.ts` mirrors the single-filename VE convention (see
12
+ // patch-vanilla-extract); `.tsx`/`.js` css variants aren't used in these repos.
13
+ const CSS_SIDE_EFFECTS = [`**/css.ts`, `**/*.css`]
14
+
15
+ /**
16
+ * Standardise `package.json#sideEffects` so tree-shaking is explicit across the monorepo.
17
+ *
18
+ * VE packages (`veInstalled`): guarantee the CSS patterns are present — `false` becomes the CSS
19
+ * array, an existing array gains any missing patterns. The array still treats every non-CSS module
20
+ * as side-effect-free, so JS tree-shaking is identical to `false`; it only rescues CSS. `true`/absent
21
+ * are left alone: they already keep every module (CSS included), and forcing an array would newly
22
+ * tree-shake the package's non-CSS side-effect imports.
23
+ *
24
+ * Non-VE packages: an absent field defaults the bundler to "everything has side effects" (no
25
+ * tree-shaking). Set `false` to opt these libraries in, matching the rest of the monorepo. A package
26
+ * that genuinely relies on a side-effect import must declare it itself (`["glob"]` or `true`) —
27
+ * any explicit value is always respected, so opting out is a one-line change in that package.
28
+ */
29
+ export const setupSideEffects = async (cwd: string, veInstalled: boolean) => {
30
+ const pkgPath = path.join(cwd, `package.json`)
31
+
32
+ let pkgJson: TF.PackageJson
33
+ try {
34
+ pkgJson = JSON.parse(await fs.readFile(pkgPath, `utf8`))
35
+ } catch (error) {
36
+ console.error(`Error reading or parsing package.json:`, error)
37
+ return
38
+ }
39
+
40
+ const current = pkgJson.sideEffects
41
+ let next: boolean | string[]
42
+
43
+ if (veInstalled) {
44
+ if (current === false) {
45
+ next = [...CSS_SIDE_EFFECTS]
46
+ } else if (Array.isArray(current)) {
47
+ next = [...current, ...CSS_SIDE_EFFECTS.filter((pattern) => !current.includes(pattern))]
48
+ } else {
49
+ return // `true`/absent already keep every module (incl. CSS) — nothing to protect
50
+ }
51
+ } else if (current === undefined) {
52
+ next = false // non-VE library opts into tree-shaking; an explicit `[…]`/`true` is respected below
53
+ } else {
54
+ return // non-VE with an explicit value — respect it
55
+ }
56
+
57
+ if (JSON.stringify(current) === JSON.stringify(next)) return
58
+
59
+ pkgJson.sideEffects = next
60
+ await fs.writeFile(pkgPath, JSON.stringify(pkgJson, null, 2) + `\n`)
61
+ console.info(`Written sideEffects: ${pkgPath}`)
62
+ execSync(`bunx oxfmt ${pkgPath}`, { cwd, stdio: `inherit` })
63
+ }
@@ -17,8 +17,10 @@ const PATCHED = String.raw`/(?:^|[/\\])css\.(js|cjs|mjs|jsx|ts|tsx)(\?used)?$/`
17
17
  * node_modules. Idempotent, and a no-op for repos without VE (e.g. backend-only). If VE is present
18
18
  * but the filter definition can't be found, it throws: a silent miss would stop every `css.ts` from
19
19
  * compiling, so a VE build change must surface at install time rather than as missing styles.
20
+ *
21
+ * @returns whether VE is installed here — the caller uses it to gate `css.ts` side-effect handling.
20
22
  */
21
- export const patchVanillaExtract = async (cwd: string) => {
23
+ export const patchVanillaExtract = async (cwd: string): Promise<boolean> => {
22
24
  const nodeModules = path.join(cwd, `node_modules`)
23
25
  const glob = new Bun.Glob(`**/@vanilla-extract/integration/dist/*.js`)
24
26
 
@@ -42,16 +44,18 @@ export const patchVanillaExtract = async (cwd: string) => {
42
44
  patched.push(rel)
43
45
  }
44
46
 
45
- if (!integrationFound) return // VE not installed here
47
+ if (!integrationFound) return false // VE not installed here
46
48
 
47
49
  if (!definitionFound) {
48
50
  throw new Error(
49
51
  `@10stars/config: found @vanilla-extract/integration but not its cssFileFilter definition — ` +
50
- `the VE build likely changed. Update ORIGINAL in src/patch-vanilla-extract.ts.`,
52
+ `the VE build likely changed. Update ORIGINAL in src/vanilla-extract/patch-vanilla-extract.ts.`,
51
53
  )
52
54
  }
53
55
 
54
56
  if (patched.length) {
55
57
  console.info(`Patched @vanilla-extract file filter to css.ts (${patched.length} file(s))`)
56
58
  }
59
+
60
+ return true
57
61
  }