@solidrt/cli 0.0.37 → 0.0.38

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidrt/cli",
3
- "version": "0.0.37",
3
+ "version": "0.0.38",
4
4
  "license": "MIT",
5
5
  "author": "Antoine van Wel",
6
6
  "type": "module",
@@ -28,17 +28,18 @@
28
28
  "zod": "^4.4.3"
29
29
  },
30
30
  "optionalDependencies": {
31
- "@solidrt/darwin-arm64": "0.0.37",
32
- "@solidrt/linux-arm64-gnu": "0.0.37",
33
- "@solidrt/linux-x64-gnu": "0.0.37",
34
- "@solidrt/win32-x64-msvc": "0.0.37"
31
+ "@solidrt/darwin-arm64": "0.0.38",
32
+ "@solidrt/linux-arm64-gnu": "0.0.38",
33
+ "@solidrt/linux-x64-gnu": "0.0.38",
34
+ "@solidrt/win32-x64-msvc": "0.0.38"
35
35
  },
36
36
  "peerDependencies": {
37
- "@solidrt/core": "0.0.37",
37
+ "@solidrt/core": "0.0.38",
38
38
  "typescript": "^7"
39
39
  },
40
40
  "devDependencies": {
41
- "@solidrt/flux-types": "0.0.37",
41
+ "@solidrt/flux-types": "0.0.38",
42
+ "@types/babel__core": "^7.20.5",
42
43
  "@types/bun": "latest"
43
44
  }
44
45
  }
@@ -117,6 +117,12 @@ Authoritative references ship inside the installed packages - read them:
117
117
  mounted. To inspect children (a typeof probe, counting), resolve them
118
118
  first with the children() helper (re-exported from @solidrt/core) and
119
119
  probe the resolved memo - never `typeof props.children` on the raw prop.
120
+ 18. Cover/contain images: give `Image` a `fit` prop ("fill" | "cover" |
121
+ "contain" | "none" | "scale-down", CSS object-fit semantics, centered)
122
+ plus a box via `layout` in any form - numbers, pct(), flex. Without
123
+ `fit`, only NUMERIC layout sizes reach the image; `width: pct(100)`
124
+ alone draws at intrinsic size. `fit="cover"` is the answer for the
125
+ ported-web hero-image/thumbnail pattern.
120
126
 
121
127
  ## Performance model (JS is the slow lane)
122
128
 
@@ -9,12 +9,12 @@
9
9
  "android": "srt client --android"
10
10
  },
11
11
  "dependencies": {
12
- "@solidrt/core": "0.0.37",
13
- "@solidrt/components": "0.0.37"
12
+ "@solidrt/core": "0.0.38",
13
+ "@solidrt/components": "0.0.38"
14
14
  },
15
15
  "devDependencies": {
16
- "@solidrt/cli": "0.0.37",
17
- "@solidrt/flux-types": "0.0.37",
16
+ "@solidrt/cli": "0.0.38",
17
+ "@solidrt/flux-types": "0.0.38",
18
18
  "typescript": "^7"
19
19
  }
20
20
  }
@@ -4,6 +4,7 @@
4
4
  "lib": ["ESNext"],
5
5
  "moduleResolution": "bundler",
6
6
  "strict": true,
7
+ "skipLibCheck": true,
7
8
  "noUncheckedIndexedAccess": true,
8
9
  "types": ["@solidrt/flux-types"]
9
10
  },
package/src/bundler.ts CHANGED
@@ -108,7 +108,12 @@ export type BundleResult = {
108
108
  export async function bundleWith(opts: BundleOptions): Promise<BundleResult | null> {
109
109
  // Define values are parsed as expressions, so string values need embedded
110
110
  // quotes - a bare word substitutes as an identifier and crashes at runtime.
111
+ // import.meta.env.DEV is the solidrt build-mode constant (used by core's
112
+ // leak sentinel; typed in core's types.d.ts). NODE_ENV stays defined as
113
+ // ecosystem compat only: third-party libraries bundled into apps commonly
114
+ // read it, and an unresolved `process` crashes at import time.
111
115
  let define: Record<string, string> = {
116
+ "import.meta.env.DEV": opts.dev ? "true" : "false",
112
117
  "process.env.NODE_ENV": opts.dev ? '"development"' : '"production"',
113
118
  }
114
119
  if (opts.devBase) define.__SRT_DEV_BASE__ = opts.devBase
@@ -1,4 +1,4 @@
1
- import { existsSync } from "node:fs"
1
+ import { existsSync, mkdirSync, rmSync } from "node:fs"
2
2
  import { dirname, join, resolve } from "node:path"
3
3
  import { source } from "../args"
4
4
  import { bundleWith } from "../bundler"
@@ -12,7 +12,7 @@ import { bundleWith } from "../bundler"
12
12
 
13
13
  // Walk up from the entry to the enclosing project (tsconfig.json or, failing
14
14
  // that, package.json).
15
- function findProjectRoot(entry: string): string | null {
15
+ export function findProjectRoot(entry: string): string | null {
16
16
  let dir = dirname(resolve(entry))
17
17
  let byConfig: string | null = null
18
18
  let byPackage: string | null = null
@@ -45,18 +45,77 @@ function parseDiagnostics(output: string): Diagnostic[] {
45
45
  return diagnostics
46
46
  }
47
47
 
48
- async function typecheck(root: string): Promise<{ app: Diagnostic[]; hidden: number } | null> {
49
- let tsc = join(root, "node_modules", ".bin", process.platform === "win32" ? "tsc.exe" : "tsc")
50
- if (!existsSync(tsc)) {
48
+ // The project's tsc, found by walking up from the project root: an example
49
+ // app can carry a tsconfig without its own node_modules (monorepo case).
50
+ function findTsc(fromDir: string): string | null {
51
+ let dir = fromDir
52
+ while (true) {
53
+ let tsc = join(dir, "node_modules", ".bin", process.platform === "win32" ? "tsc.exe" : "tsc")
54
+ if (existsSync(tsc)) return tsc
55
+ let parent = dirname(dir)
56
+ if (parent === dir) return null
57
+ dir = parent
58
+ }
59
+ }
60
+
61
+ // Typecheck the entry's program, not the enclosing project: a transient
62
+ // config extends the project's tsconfig and roots the program at the entry
63
+ // alone, so tsc checks exactly the entry's import closure - unrelated files
64
+ // are excluded by construction. The config lives in the project-local
65
+ // .srt-data (the dev-artifact dir; absolute paths inside, so its location
66
+ // only matters for type-package resolution, which walks up to the project's
67
+ // node_modules from there).
68
+ export async function typecheck(root: string, entry: string): Promise<{ app: Diagnostic[]; hidden: number } | null> {
69
+ let tsconfig = join(root, "tsconfig.json")
70
+ if (!existsSync(tsconfig)) {
71
+ console.warn("Typecheck skipped: no tsconfig.json above the entry")
72
+ return null
73
+ }
74
+ let tsc = findTsc(root)
75
+ if (!tsc) {
51
76
  console.warn("Typecheck skipped: no tsc in the project (add the typescript devDependency)")
52
77
  return null
53
78
  }
54
- let proc = Bun.spawn([tsc, "--noEmit", "--pretty", "false"], { cwd: root, stdout: "pipe", stderr: "pipe" })
55
- let [out, err] = await Promise.all([new Response(proc.stdout).text(), new Response(proc.stderr).text()])
56
- await proc.exited
57
- let diagnostics = parseDiagnostics(out + err)
58
- let app = diagnostics.filter((d) => !d.inDependencies)
59
- return { app, hidden: diagnostics.length - app.length }
79
+ let dataDir = join(root, ".srt-data")
80
+ mkdirSync(dataDir, { recursive: true })
81
+ let config = join(dataDir, `typecheck-${process.pid}.tsconfig.json`)
82
+ // include: [] overrides any include inherited from the extended config -
83
+ // files and include are unioned, so without this a base config's include
84
+ // would drag the whole project back into the program.
85
+ await Bun.write(config, JSON.stringify({ extends: tsconfig, include: [], files: [resolve(entry)] }))
86
+ try {
87
+ let proc = Bun.spawn([tsc, "-p", config, "--noEmit", "--pretty", "false"], {
88
+ cwd: root,
89
+ stdout: "pipe",
90
+ stderr: "pipe",
91
+ })
92
+ let [out, err] = await Promise.all([new Response(proc.stdout).text(), new Response(proc.stderr).text()])
93
+ await proc.exited
94
+ let diagnostics = parseDiagnostics(out + err)
95
+ let app = diagnostics.filter((d) => !d.inDependencies)
96
+ return { app, hidden: diagnostics.length - app.length }
97
+ } finally {
98
+ rmSync(config, { force: true })
99
+ }
100
+ }
101
+
102
+ // Print a typecheck result (diagnostics, then the one-line verdict) and return
103
+ // whether app-code errors were found. Callers pass repl-aware printers when
104
+ // the output lands over a live prompt (the dev-server startup check).
105
+ export function reportTypes(
106
+ types: { app: Diagnostic[]; hidden: number },
107
+ log: (...args: any[]) => void = console.log,
108
+ error: (...args: any[]) => void = console.error,
109
+ ): boolean {
110
+ for (let d of types.app) error(d.lines.join("\n"))
111
+ if (types.app.length > 0) {
112
+ let hidden = types.hidden > 0 ? ` (${types.hidden} in dependencies hidden)` : ""
113
+ error(`${types.app.length} type error${types.app.length === 1 ? "" : "s"} in app code${hidden}`)
114
+ return true
115
+ }
116
+ if (types.hidden > 0) log(`Types OK (${types.hidden} dependency-internal errors hidden)`)
117
+ else log("Types OK")
118
+ return false
60
119
  }
61
120
 
62
121
  export async function runCheckCommand() {
@@ -73,19 +132,8 @@ export async function runCheckCommand() {
73
132
  if (!root) {
74
133
  console.warn("Typecheck skipped: no tsconfig.json or package.json above the entry")
75
134
  } else {
76
- let types = await typecheck(root)
77
- if (types) {
78
- for (let d of types.app) console.error(d.lines.join("\n"))
79
- if (types.app.length > 0) {
80
- failed = true
81
- let hidden = types.hidden > 0 ? ` (${types.hidden} in dependencies hidden)` : ""
82
- console.error(`${types.app.length} type error${types.app.length === 1 ? "" : "s"} in app code${hidden}`)
83
- } else if (types.hidden > 0) {
84
- console.log(`Types OK (${types.hidden} dependency-internal errors hidden)`)
85
- } else {
86
- console.log("Types OK")
87
- }
88
- }
135
+ let types = await typecheck(root, entry)
136
+ if (types && reportTypes(types)) failed = true
89
137
  }
90
138
 
91
139
  if (failed) process.exit(1)
@@ -1,6 +1,7 @@
1
1
  import pkg from "../../package.json"
2
2
  import { source, isSource, isPrebuilt, values } from "../args"
3
- import { state, shutdown } from "../util"
3
+ import { state, shutdown, print, printErr } from "../util"
4
+ import { findProjectRoot, typecheck, reportTypes } from "./check"
4
5
  import { bundle } from "../bundler"
5
6
  import { buildManifest, projectDirFor } from "../project"
6
7
  import { startServer, buildReload, sendReload, showBuildFailure } from "../dev-server"
@@ -53,4 +54,17 @@ export async function runServerCommand() {
53
54
  console.log(`[cli] Welcome to SolidRT${version}!`)
54
55
  startRepl()
55
56
  startWatcher()
57
+
58
+ // Startup typecheck, deliberately not awaited: diagnostics print over the
59
+ // repl when tsc finishes, and a type error never gates the boot (srt check
60
+ // is the hard gate). Once per server lifetime; hot reloads never typecheck.
61
+ // Source builds only: a prebuilt .srt.js has no checkable project here.
62
+ if (source && isSource) startupTypecheck(source)
63
+ }
64
+
65
+ async function startupTypecheck(entry: string) {
66
+ let root = findProjectRoot(entry)
67
+ if (!root) return
68
+ let types = await typecheck(root, entry)
69
+ if (types) reportTypes(types, print, printErr)
56
70
  }
package/src/dev-server.ts CHANGED
@@ -82,7 +82,8 @@ export async function watchAllowed(): Promise<boolean> {
82
82
  try {
83
83
  let resp = await fetch(`${INTERNAL_BASE}/watch`)
84
84
  if (!resp.ok) return true
85
- return (await resp.json()).enabled !== false
85
+ let data = (await resp.json()) as { enabled?: boolean }
86
+ return data.enabled !== false
86
87
  } catch {
87
88
  return true
88
89
  }
@@ -100,7 +101,7 @@ export type ClientEntry = {
100
101
  export async function getClients(): Promise<ClientEntry[]> {
101
102
  let resp = await fetch(`${INTERNAL_BASE}/clients`)
102
103
  if (!resp.ok) throw new Error(`Dev server /clients failed: ${resp.status}`)
103
- return resp.json()
104
+ return resp.json() as Promise<ClientEntry[]>
104
105
  }
105
106
 
106
107
  // Reload code that fails to start the engine on purpose. The runtime treats a
package/src/project.ts CHANGED
@@ -154,7 +154,9 @@ export function collectAssets(entry: string): { assets: ManifestAsset[]; fonts:
154
154
  export function loadAppIdentity(sourcePath: string): AppIdentity {
155
155
  let project = findProjectPackage(sourcePath)
156
156
  let config = project?.pkg.solidrt ?? {}
157
- let fallbackName = project?.pkg.name ?? basename(sourcePath).replace(/\.[jt]sx?$/, "")
157
+ // A scoped package name (@org/name) defaults to its last segment: identity
158
+ // fields reject path separators, and derived defaults must never fail that.
159
+ let fallbackName = (project?.pkg.name ?? basename(sourcePath).replace(/\.[jt]sx?$/, "")).split("/").pop()!
158
160
 
159
161
  for (let key of ["appId", "org", "displayName"]) {
160
162
  if (key in config && typeof config[key] !== "string") fail(`"solidrt": "${key}" must be a string`)
@@ -170,7 +172,7 @@ export function loadAppIdentity(sourcePath: string): AppIdentity {
170
172
  fail(`"solidrt": "appId" must match ${APP_ID_PATTERN} (reverse-DNS recommended, e.g. "com.example.app")`)
171
173
  }
172
174
  }
173
- let displayName = config.displayName ?? (project?.pkg.name || fallbackName)
175
+ let displayName = config.displayName ?? fallbackName
174
176
  let org = config.org ?? displayName
175
177
  checkField(appId, '"appId"')
176
178
  checkField(displayName, '"displayName"')
@@ -0,0 +1,18 @@
1
+ // Packages with no bundled or DefinitelyTyped declarations. @babel/core is
2
+ // covered by @types/babel__core; plugins and presets have no dedicated types
3
+ // by convention - they are consumed as opaque PluginItem values.
4
+ declare module "@babel/plugin-syntax-jsx" {
5
+ import { type PluginItem } from "@babel/core"
6
+ let plugin: PluginItem
7
+ export default plugin
8
+ }
9
+ declare module "@babel/preset-typescript" {
10
+ import { type PluginItem } from "@babel/core"
11
+ let preset: PluginItem
12
+ export default preset
13
+ }
14
+ declare module "babel-preset-solid" {
15
+ import { type PluginItem } from "@babel/core"
16
+ let preset: PluginItem
17
+ export default preset
18
+ }