@fusion.dev/create-helix-app 0.25.1 → 0.26.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 (3) hide show
  1. package/README.md +6 -0
  2. package/bin.mjs +87 -7
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -24,6 +24,12 @@ This is `helix new` from
24
24
  reached from somewhere with no project yet. A project that already has Helix
25
25
  installed can run `npx helix new` instead, and gets the same templates.
26
26
 
27
+ **It says when a newer Helix is out.** Without `@latest`, npm can run a copy it
28
+ cached earlier, so this asks npm for the newest version while it writes the
29
+ project and prints one line if it isn't this one. It waits three seconds at most
30
+ and says nothing if npm doesn't answer. It doesn't ask in CI, from a
31
+ pre-release, or when `HELIX_NO_UPDATE_CHECK` is set.
32
+
27
33
  **Scoped because the plain name is taken.** `create-helix-app` on npm belongs to
28
34
  an unrelated project, so `npm create helix-app` runs somebody else's code.
29
35
  `npm create @fusion.dev/helix-app` resolves to this package and no other.
package/bin.mjs CHANGED
@@ -16,22 +16,102 @@
16
16
  * own cache. So this hands over the package it resolved, which is the one
17
17
  * versioned with the binary beside it. `HELIX_TEMPLATES` already set wins, as it
18
18
  * does everywhere else it is read.
19
+ *
20
+ * **And it says when it is not the newest**, which the binary cannot: it has no
21
+ * HTTP client, by design, and this door is the one that goes stale. Asked
22
+ * without `@latest`, npm runs whatever copy it cached last time, so a reader can
23
+ * scaffold from a release months old without anything saying so. See `newer`.
19
24
  */
20
- import { spawnSync } from 'node:child_process'
25
+ import { spawn } from 'node:child_process'
26
+ import { readFileSync } from 'node:fs'
21
27
  import { createRequire } from 'node:module'
22
- import { dirname } from 'node:path'
28
+ import { dirname, join } from 'node:path'
29
+ import { fileURLToPath } from 'node:url'
23
30
 
24
31
  const require = createRequire(import.meta.url)
25
32
  const templates = dirname(require.resolve('@fusion.dev/helix-templates/templates.json'))
26
33
  const helix = require.resolve('@fusion.dev/helix-cli/bin.mjs')
34
+ const own = JSON.parse(
35
+ readFileSync(join(dirname(fileURLToPath(import.meta.url)), 'package.json'), 'utf8'),
36
+ )
37
+
38
+ /** How long the registry gets, since a scaffold must never wait on it. */
39
+ const PATIENCE = 3000
40
+
41
+ /** `[major, minor, patch]`, or undefined for a pre-release or anything else. */
42
+ const release = (version) => {
43
+ const found = /^(\d+)\.(\d+)\.(\d+)$/.exec(String(version).trim())
44
+ return found ? found.slice(1).map(Number) : undefined
45
+ }
46
+
47
+ /**
48
+ * The version newer than this one that npm calls latest, or undefined.
49
+ *
50
+ * **Asked through npm rather than over HTTP**, because npm holds the reader's
51
+ * login and these packages are restricted until they are public: an anonymous
52
+ * request is answered 404 for a package that exists. npm is on the path by
53
+ * definition, this having been started by it.
54
+ *
55
+ * **Never in the way.** Given three seconds and killed after them, and every
56
+ * failure is silence: offline, refused, slow, or answering something that is not
57
+ * a version. Not asked in CI, where nobody reads it, nor from a pre-release,
58
+ * which somebody chose on purpose, nor where `HELIX_NO_UPDATE_CHECK` is set.
59
+ */
60
+ const newer = () => {
61
+ const mine = release(own.version)
62
+ if (mine === undefined || process.env.CI || process.env.HELIX_NO_UPDATE_CHECK) {
63
+ return Promise.resolve(undefined)
64
+ }
65
+ return new Promise((resolve) => {
66
+ let said = ''
67
+ const asking = spawn('npm', ['view', `${own.name}@latest`, 'version'], {
68
+ shell: process.platform === 'win32',
69
+ stdio: ['ignore', 'pipe', 'ignore'],
70
+ })
71
+ const done = (answer) => {
72
+ clearTimeout(timer)
73
+ resolve(answer)
74
+ }
75
+ const timer = setTimeout(() => {
76
+ asking.kill()
77
+ done(undefined)
78
+ }, PATIENCE)
79
+ asking.stdout.on('data', (chunk) => {
80
+ said += chunk
81
+ })
82
+ asking.on('error', () => done(undefined))
83
+ asking.on('close', (code) => {
84
+ const latest = release(said)
85
+ if (code !== 0 || latest === undefined) {
86
+ done(undefined)
87
+ return
88
+ }
89
+ const ahead = latest.findIndex((part, index) => part !== mine[index])
90
+ done(ahead !== -1 && latest[ahead] > mine[ahead] ? said.trim() : undefined)
91
+ })
92
+ })
93
+ }
94
+
95
+ /* Asked while the project is written rather than before, so a reader waits on
96
+ the registry only if it is slower than the scaffold. */
97
+ const checking = newer()
27
98
 
28
- const { status, error } = spawnSync(process.execPath, [helix, 'new', ...process.argv.slice(2)], {
99
+ const running = spawn(process.execPath, [helix, 'new', ...process.argv.slice(2)], {
29
100
  env: { ...process.env, HELIX_TEMPLATES: process.env.HELIX_TEMPLATES ?? templates },
30
101
  stdio: 'inherit',
31
102
  })
32
- /* A status means it ran, whatever `error` says: see the same line in the shim. */
33
- if (error && status === null) {
103
+ running.on('error', (error) => {
34
104
  console.error(`create-helix-app: could not run helix: ${error.message}`)
35
105
  process.exit(70)
36
- }
37
- process.exit(status ?? 70)
106
+ })
107
+ running.on('close', async (status) => {
108
+ /* On stderr, so `--json` on stdout stays a document a script can read. */
109
+ const latest = await checking
110
+ if (latest !== undefined) {
111
+ console.error(
112
+ `\nHelix ${latest} is out, and this is ${own.version}. For the newest templates:\n\n` +
113
+ ` npm create ${own.name.replace('create-', '')}@latest\n`,
114
+ )
115
+ }
116
+ process.exit(status ?? 70)
117
+ })
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fusion.dev/create-helix-app",
3
3
  "description": "Starts a Helix project: `npm create @fusion.dev/helix-app`, which is `helix new` for somebody with no project yet.",
4
- "version": "0.25.1",
4
+ "version": "0.26.0",
5
5
  "license": "MIT",
6
6
  "author": "Ross Sheppard <ross.sheppard@fusion.dev>",
7
7
  "repository": {
@@ -21,8 +21,8 @@
21
21
  },
22
22
  "bugs": "https://github.com/project-fusion/fusion/issues",
23
23
  "dependencies": {
24
- "@fusion.dev/helix-cli": "0.25.1",
25
- "@fusion.dev/helix-templates": "0.25.1"
24
+ "@fusion.dev/helix-cli": "0.26.0",
25
+ "@fusion.dev/helix-templates": "0.26.0"
26
26
  },
27
27
  "homepage": "https://fusion.dev",
28
28
  "keywords": [