create-stator 1.3.0 → 1.5.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.
Files changed (2) hide show
  1. package/index.js +57 -3
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -42,13 +42,18 @@ const TEMPLATES = [
42
42
  label: 'With auth',
43
43
  hint: 'accounts, guarded login, roles, session rotation (Node 24+)',
44
44
  },
45
+ {
46
+ value: 'weather',
47
+ label: 'Weather',
48
+ hint: 'live forecasts over SSE, entry-effect data loading, a canvas island',
49
+ },
45
50
  ]
46
51
 
47
52
  /** First-party templates resolve into the monorepo's examples/. */
48
53
  const FIRST_PARTY = 'gh:statorjs/stator/examples'
49
54
  /** Scaffolded apps get a real semver for the framework (the examples
50
55
  * themselves use workspace linking in-repo). Bumped with releases. */
51
- const STATOR_RANGE = '^1.1.0'
56
+ const STATOR_RANGE = '^1.4.0'
52
57
 
53
58
  const { values: flags, positionals } = parseArgs({
54
59
  allowPositionals: true,
@@ -142,8 +147,18 @@ if (!template) {
142
147
  }
143
148
 
144
149
  // --- fetch ---
145
- const ref = flags.ref ? `#${flags.ref}` : ''
146
- const source = isRemoteSource(template) ? template : `${FIRST_PARTY}/${template}${ref}`
150
+ // Source and ref are held separately so provenance can record them apart:
151
+ // community sources may embed a ref (`github:user/repo/path#branch`), while
152
+ // first-party templates take it from --ref.
153
+ const sourceBase = (isRemoteSource(template) ? template : `${FIRST_PARTY}/${template}`).split(
154
+ '#',
155
+ )[0]
156
+ const refName = isRemoteSource(template)
157
+ ? template.includes('#')
158
+ ? template.slice(template.indexOf('#') + 1)
159
+ : null
160
+ : (flags.ref ?? null)
161
+ const source = refName ? `${sourceBase}#${refName}` : sourceBase
147
162
  const s = p.spinner()
148
163
  s.start(`Fetching ${template}`)
149
164
  try {
@@ -170,6 +185,18 @@ try {
170
185
  pkg.dependencies['@statorjs/stator'] = STATOR_RANGE
171
186
  }
172
187
  delete pkg.private
188
+ // Record where this scaffold came from. The commit is the merge base for a
189
+ // later template update (docs: recipes/updating-from-a-template) — the one
190
+ // fact that can only be captured now. Best-effort: null when offline.
191
+ pkg.stator = {
192
+ template: {
193
+ v: 1,
194
+ source: sourceBase,
195
+ ref: refName,
196
+ commit: resolveTemplateCommit(sourceBase, refName),
197
+ scaffoldedAt: new Date().toISOString(),
198
+ },
199
+ }
173
200
  await writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`)
174
201
  s.stop(`Scaffolded ${name} (${template})`)
175
202
  } catch {
@@ -231,3 +258,30 @@ function bailIfCancelled(v) {
231
258
  process.exit(0)
232
259
  }
233
260
  }
261
+
262
+ /** Resolve the template ref to a commit sha for provenance. Best-effort by
263
+ * design: no git, no network, or an unknown provider all return null rather
264
+ * than slow down or fail the scaffold. Annotated tags resolve to the peeled
265
+ * commit (`^{}`) when the host lists one. */
266
+ function resolveTemplateCommit(src, refName) {
267
+ if (refName && /^[0-9a-f]{40}$/.test(refName)) return refName
268
+ // giget providers that are also plain git hosts, for `git ls-remote`.
269
+ const hosts = {
270
+ gh: 'github.com',
271
+ github: 'github.com',
272
+ gitlab: 'gitlab.com',
273
+ bitbucket: 'bitbucket.org',
274
+ }
275
+ const m = src.match(/^([a-z]+):([^/]+\/[^/#]+)/)
276
+ const host = m && hosts[m[1]]
277
+ if (!host) return null
278
+ const out = spawnSync('git', ['ls-remote', `https://${host}/${m[2]}`, refName ?? 'HEAD'], {
279
+ encoding: 'utf8',
280
+ timeout: 15_000,
281
+ })
282
+ if (out.status !== 0 || !out.stdout) return null
283
+ const lines = out.stdout.trim().split('\n')
284
+ const line = lines.find((l) => l.includes('^{}')) ?? lines[0]
285
+ const sha = line?.split(/\s+/)[0]
286
+ return sha && /^[0-9a-f]{40}$/.test(sha) ? sha : null
287
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-stator",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "Scaffold a new Stator app: pnpm create stator my-app",
5
5
  "license": "MIT",
6
6
  "type": "module",