create-stator 1.4.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 +51 -2
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -147,8 +147,18 @@ if (!template) {
147
147
  }
148
148
 
149
149
  // --- fetch ---
150
- const ref = flags.ref ? `#${flags.ref}` : ''
151
- 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
152
162
  const s = p.spinner()
153
163
  s.start(`Fetching ${template}`)
154
164
  try {
@@ -175,6 +185,18 @@ try {
175
185
  pkg.dependencies['@statorjs/stator'] = STATOR_RANGE
176
186
  }
177
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
+ }
178
200
  await writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`)
179
201
  s.stop(`Scaffolded ${name} (${template})`)
180
202
  } catch {
@@ -236,3 +258,30 @@ function bailIfCancelled(v) {
236
258
  process.exit(0)
237
259
  }
238
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.4.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",