@invisibleloop/pulse 0.20.56 → 0.20.58

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": "@invisibleloop/pulse",
3
- "version": "0.20.56",
3
+ "version": "0.20.58",
4
4
  "type": "module",
5
5
  "description": "AI-first frontend framework. The spec is the source of truth.",
6
6
  "license": "MIT",
@@ -76,7 +76,7 @@
76
76
  "scripts": {
77
77
  "dev": "node --watch src/cli/dev.js",
78
78
  "build": "node scripts/build.js",
79
- "test": "node src/spec/schema.test.js && node src/runtime/runtime.test.js && node src/runtime/navigate.test.js && node src/runtime/morph.test.js && node src/runtime/ssr.test.js && node src/runtime/image.test.js && node src/server/server.test.js && node src/cli/cli.test.js && node src/cli/validate.test.js && node src/cli/diagnose.test.js && node src/cli/check-bundles.test.js && node src/cli/review.test.js && node src/cli/process-utils.test.js && node src/cli/stamp.test.js && node src/cli/feedback.test.js && node src/cli/update.test.js && node src/cli/dev-host.test.js && node src/ui/ui.test.js && node src/testing/testing.test.js && node src/store/store.test.js && node src/md/md.test.js && node scripts/strip-server.test.js && node scripts/css-purge.test.js && node scripts/release-version.test.js && node src/mcp/mcp.test.js && node src/mcp/guards.test.js",
79
+ "test": "node src/spec/schema.test.js && node src/runtime/runtime.test.js && node src/runtime/navigate.test.js && node src/runtime/morph.test.js && node src/runtime/ssr.test.js && node src/runtime/image.test.js && node src/server/server.test.js && node src/cli/cli.test.js && node src/cli/validate.test.js && node src/cli/diagnose.test.js && node src/cli/check-bundles.test.js && node src/cli/review.test.js && node src/cli/process-utils.test.js && node src/cli/stamp.test.js && node src/cli/feedback.test.js && node src/cli/update.test.js && node src/cli/dev-host.test.js && node src/cli/dev-reload.test.js && node src/ui/ui.test.js && node src/testing/testing.test.js && node src/store/store.test.js && node src/md/md.test.js && node scripts/strip-server.test.js && node scripts/css-purge.test.js && node scripts/release-version.test.js && node src/mcp/mcp.test.js && node src/mcp/guards.test.js",
80
80
  "typecheck": "tsc --noEmit",
81
81
  "docs": "node --watch src/cli/dev.js --root ../../docs --port 4000",
82
82
  "docs:start": "node ../../docs/server.js"
@@ -1 +1 @@
1
- 0.20.56
1
+ 0.20.58
package/src/cli/dev.js CHANGED
@@ -10,6 +10,8 @@
10
10
 
11
11
  import path from 'path'
12
12
  import fs from 'fs'
13
+ import { register } from 'node:module'
14
+ import { pathToFileURL } from 'node:url'
13
15
  import { createServer } from '../server/index.js'
14
16
  import { loadPages, loadStore } from './discover.js'
15
17
  import * as log from './logger.js'
@@ -31,6 +33,14 @@ const ROOT = rootArg !== -1
31
33
  // resolve to the project root, not wherever the parent process was launched from.
32
34
  if (process.cwd() !== ROOT) process.chdir(ROOT)
33
35
 
36
+ // Hot reload re-imports each page with a cache-busting stamp; this hook passes the stamp on
37
+ // to the project modules the page imports, so editing src/lib or src/components takes
38
+ // effect too (see reload-hooks.js). realpath: Node reports resolved URLs without symlinks.
39
+ register('./reload-hooks.js', {
40
+ parentURL: import.meta.url,
41
+ data: { rootUrl: pathToFileURL(fs.realpathSync(ROOT)).href + '/' },
42
+ })
43
+
34
44
  let _config = {}
35
45
  const configPath = path.join(ROOT, 'pulse.config.js')
36
46
  if (fs.existsSync(configPath)) {
@@ -14,6 +14,7 @@
14
14
 
15
15
  import fs from 'fs'
16
16
  import path from 'path'
17
+ import { pathToFileURL } from 'url'
17
18
 
18
19
  /**
19
20
  * Derive a route from a file path relative to the pages directory.
@@ -136,7 +137,7 @@ export async function loadStore(projectRoot, cacheBust = null) {
136
137
  const storePath = path.join(projectRoot, 'pulse.store.js')
137
138
  if (!fs.existsSync(storePath)) return null
138
139
 
139
- const url = 'file://' + storePath + (cacheBust ? `?t=${cacheBust}` : '')
140
+ const url = pathToFileURL(storePath).href + (cacheBust ? `?t=${cacheBust}` : '')
140
141
  const mod = await import(url)
141
142
  const def = mod.default
142
143
  if (!def || typeof def !== 'object') {
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Pulse — module-loader hook for dev hot reload
3
+ *
4
+ * `pulse dev` re-imports each page with `?t=<stamp>` to dodge Node's ESM cache, but that
5
+ * only refreshes the page file: everything the page imports (src/lib, src/components, ...)
6
+ * resolves to the same URL as before and comes back stale from the cache. This hook gives
7
+ * a project module imported by a stamped module the same stamp, so the whole project
8
+ * graph is re-evaluated on a reload.
9
+ *
10
+ * Only the project's own files are stamped. Anything in node_modules, the framework
11
+ * itself, and `node:` builtins keep their URL, so packages (and framework singletons)
12
+ * are evaluated once rather than once per edit.
13
+ *
14
+ * Limits: CommonJS files (.cjs) are cached by path, so the stamp cannot refresh them, and
15
+ * modules outside the project root (e.g. a sibling workspace package) are neither stamped
16
+ * nor watched.
17
+ */
18
+
19
+ let rootUrl = ''
20
+
21
+ export function initialize(data) {
22
+ rootUrl = data.rootUrl
23
+ }
24
+
25
+ export async function resolve(specifier, context, nextResolve) {
26
+ const result = await nextResolve(specifier, context)
27
+ const url = result.url
28
+ if (!rootUrl || !url.startsWith(rootUrl) || url.includes('?')) return result
29
+ if (url.slice(rootUrl.length).includes('node_modules/')) return result // below the root: a project may itself live under a node_modules folder
30
+ if (!context.parentURL?.startsWith(rootUrl)) return result
31
+
32
+ const stamp = new URL(context.parentURL).searchParams.get('t')
33
+ if (!stamp || !/^\d+$/.test(stamp)) return result
34
+ return { ...result, url: `${url}?t=${stamp}` }
35
+ }
@@ -462,6 +462,7 @@ function devImportMap(nonce) {
462
462
  return `<script type="importmap" nonce="${nonce}">
463
463
  {
464
464
  "imports": {
465
+ "@invisibleloop/pulse/html": "/@pulse/html.js",
465
466
  "@invisibleloop/pulse/image": "/@pulse/runtime/image.js",
466
467
  "@invisibleloop/pulse/ui": "/@pulse/ui/index.js",
467
468
  "@invisibleloop/pulse/md": "/@pulse/md/browser-stub.js"