@woodsportal/hubspot-kit 1.0.36 → 1.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/CHANGELOG.md +10 -0
- package/package.json +2 -2
- package/scripts/resolve-consumer-dep.mjs +76 -11
- package/scripts/resolve-consumer-dep.test.mjs +22 -0
- package/scripts/test-fixtures/consumer-dep/node_modules/@tanstack/router-plugin/dist/esm/vite.js +3 -0
- package/scripts/test-fixtures/consumer-dep/node_modules/@tanstack/router-plugin/package.json +16 -0
- package/scripts/test-fixtures/consumer-dep/node_modules/react/jsx-dev-runtime.js +1 -0
- package/scripts/test-fixtures/consumer-dep/node_modules/react/jsx-runtime.js +1 -0
- package/scripts/test-fixtures/consumer-dep/node_modules/react/package.json +8 -0
- package/scripts/test-fixtures/consumer-dep/node_modules/vite/dist/node/index.js +1 -0
- package/scripts/test-fixtures/consumer-dep/node_modules/vite/package.json +10 -0
- package/scripts/test-fixtures/consumer-dep-fixture-root.mjs +8 -0
- package/scripts/vite/consumer-react-aliases.js +2 -5
- package/scripts/vite/consumer-react-aliases.test.mjs +3 -23
- package/scripts/vite/resolve-from-project-root.js +22 -5
- package/scripts/vite/resolve-from-project-root.test.mjs +37 -0
package/CHANGELOG.md
CHANGED
|
@@ -19,6 +19,16 @@
|
|
|
19
19
|
- **CSS entries** — shared `tailwind.css.config.css`; documented never-merge rules on `main.dev.css` / `main.prod.css`
|
|
20
20
|
- **`wp doctor`** — CSS pipeline checks (prefix, `.temp` @source, `skipRuntimeCss`, `module.css` dw utilities, CDN `require_css` warning)
|
|
21
21
|
|
|
22
|
+
## 1.0.38 — 2026-06-08
|
|
23
|
+
|
|
24
|
+
### Dev server
|
|
25
|
+
- **Module-config overlay** — stop force-resolving `react` / `react-dom` to CJS `index.js` for kit UI under `.wphs/`; fixes `createContext` / named export errors in the browser on `yarn local`
|
|
26
|
+
|
|
27
|
+
## 1.0.37 — 2026-06-08
|
|
28
|
+
|
|
29
|
+
### Dev server
|
|
30
|
+
- **Consumer dep resolution** — replace `createRequire` / `require.resolve` with a filesystem `package.json` exports resolver for Vite, React plugins, and JSX runtime aliases; fixes `getStatus` crash on Node 24+ when starting `yarn local`
|
|
31
|
+
|
|
22
32
|
## 1.0.36 — 2026-06-08
|
|
23
33
|
|
|
24
34
|
### Dev server
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@woodsportal/hubspot-kit",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.38",
|
|
4
4
|
"description": "WoodsCLI — HubSpot CMS dev kit by WoodsPortal. wp CLI, Vite presets, module-config overlay, and theme/module build pipelines.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"build": "tsup",
|
|
52
52
|
"dev": "tsup --watch",
|
|
53
53
|
"typecheck": "tsc --noEmit",
|
|
54
|
-
"test": "node --import tsx --test 'src/lib/**/*.test.ts' 'src/schema/**/*.test.ts' 'src/vite/**/*.test.ts' 'scripts/portal-build-id.test.mjs' 'scripts/vite/ensure-dev-kit-shims.test.mjs' 'scripts/vite/consumer-react-aliases.test.mjs' && vitest run src/dev-module-config/__tests__/normalize-repeater-items.test.ts src/dev-module-config/__tests__/module-config-transform.test.ts src/dev-module-config/__tests__/flatten-visible-fields.test.ts",
|
|
54
|
+
"test": "node --import tsx --test 'src/lib/**/*.test.ts' 'src/schema/**/*.test.ts' 'src/vite/**/*.test.ts' 'scripts/portal-build-id.test.mjs' 'scripts/resolve-consumer-dep.test.mjs' 'scripts/vite/ensure-dev-kit-shims.test.mjs' 'scripts/vite/consumer-react-aliases.test.mjs' 'scripts/vite/resolve-from-project-root.test.mjs' && vitest run src/dev-module-config/__tests__/normalize-repeater-items.test.ts src/dev-module-config/__tests__/module-config-transform.test.ts src/dev-module-config/__tests__/flatten-visible-fields.test.ts",
|
|
55
55
|
"test:examples": "node examples/smoke/run-matrix.mjs --tier static",
|
|
56
56
|
"lint": "eslint src",
|
|
57
57
|
"verify": "npm run typecheck && npm run test && npm run test:examples && npm run build",
|
|
@@ -1,25 +1,90 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Resolve and import packages from the consumer project (woodscli.json root).
|
|
3
|
-
*
|
|
3
|
+
* Pure filesystem resolution — avoids createRequire (Node 24+ getStatus) and
|
|
4
|
+
* import.meta.resolve from kit context (wrong node_modules).
|
|
4
5
|
*/
|
|
5
|
-
import {
|
|
6
|
-
import { resolve } from 'node:path'
|
|
6
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
7
|
+
import { resolve, sep } from 'node:path'
|
|
7
8
|
import { pathToFileURL } from 'node:url'
|
|
9
|
+
|
|
8
10
|
import { findProjectRoot } from './project-root.mjs'
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
|
-
* @param {string} specifier
|
|
13
|
+
* @param {string} specifier
|
|
14
|
+
* @param {string} projectRoot
|
|
15
|
+
*/
|
|
16
|
+
function readPkgJson(projectRoot, pkgName) {
|
|
17
|
+
const pkgJsonPath = resolve(projectRoot, 'node_modules', ...pkgName.split('/'), 'package.json')
|
|
18
|
+
if (!existsSync(pkgJsonPath)) {
|
|
19
|
+
throw new Error(`Cannot find package '${pkgName}' under ${projectRoot}`)
|
|
20
|
+
}
|
|
21
|
+
return { pkgJsonPath, pkg: JSON.parse(readFileSync(pkgJsonPath, 'utf8')) }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {string} pkgRoot
|
|
26
|
+
* @param {string} target
|
|
27
|
+
*/
|
|
28
|
+
function resolveTargetFile(pkgRoot, target) {
|
|
29
|
+
const rel = target.replace(/^\.\//, '')
|
|
30
|
+
const abs = resolve(pkgRoot, rel)
|
|
31
|
+
if (existsSync(abs)) return abs
|
|
32
|
+
if (existsSync(`${abs}.js`)) return `${abs}.js`
|
|
33
|
+
if (existsSync(`${abs}.mjs`)) return `${abs}.mjs`
|
|
34
|
+
if (existsSync(resolve(abs, 'index.js'))) return resolve(abs, 'index.js')
|
|
35
|
+
return abs
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {unknown} exportEntry
|
|
40
|
+
* @returns {string | null}
|
|
12
41
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
42
|
+
function pickExportTarget(exportEntry) {
|
|
43
|
+
if (typeof exportEntry === 'string') return exportEntry
|
|
44
|
+
if (!exportEntry || typeof exportEntry !== 'object') return null
|
|
45
|
+
|
|
46
|
+
const record = /** @type {Record<string, unknown>} */ (exportEntry)
|
|
47
|
+
for (const key of ['import', 'default', 'node', 'require', 'module-sync']) {
|
|
48
|
+
const val = record[key]
|
|
49
|
+
if (typeof val === 'string') return val
|
|
50
|
+
const nested = pickExportTarget(val)
|
|
51
|
+
if (nested) return nested
|
|
52
|
+
}
|
|
53
|
+
return null
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @param {string} specifier e.g. vite, @vitejs/plugin-react, react/jsx-dev-runtime
|
|
58
|
+
* @param {string} projectRoot
|
|
59
|
+
*/
|
|
60
|
+
export function resolveConsumerDep(specifier, projectRoot = findProjectRoot()) {
|
|
61
|
+
const segments = specifier.split('/')
|
|
62
|
+
const pkgName = segments[0].startsWith('@')
|
|
63
|
+
? `${segments[0]}/${segments[1]}`
|
|
64
|
+
: segments[0]
|
|
65
|
+
const subpath = segments[0].startsWith('@') ? segments.slice(2).join('/') : segments.slice(1).join('/')
|
|
66
|
+
const pkgRoot = resolve(projectRoot, 'node_modules', ...pkgName.split('/'))
|
|
67
|
+
const { pkg } = readPkgJson(projectRoot, pkgName)
|
|
68
|
+
|
|
69
|
+
if (!subpath) {
|
|
70
|
+
const rootExport = pickExportTarget(pkg.exports?.['.']) ?? pkg.module ?? pkg.main ?? 'index.js'
|
|
71
|
+
return resolveTargetFile(pkgRoot, rootExport)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const exportKey = `./${subpath}`
|
|
75
|
+
const exportTarget = pickExportTarget(pkg.exports?.[exportKey])
|
|
76
|
+
if (exportTarget) {
|
|
77
|
+
return resolveTargetFile(pkgRoot, exportTarget)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return resolveTargetFile(pkgRoot, subpath)
|
|
17
81
|
}
|
|
18
82
|
|
|
19
83
|
/**
|
|
20
84
|
* @param {string} specifier
|
|
85
|
+
* @param {string} [projectRoot]
|
|
21
86
|
*/
|
|
22
|
-
export async function importConsumerDep(specifier) {
|
|
23
|
-
const
|
|
24
|
-
return import(pathToFileURL(
|
|
87
|
+
export async function importConsumerDep(specifier, projectRoot = findProjectRoot()) {
|
|
88
|
+
const abs = resolveConsumerDep(specifier, projectRoot)
|
|
89
|
+
return import(pathToFileURL(abs).href)
|
|
25
90
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { describe, it } from 'node:test'
|
|
3
|
+
|
|
4
|
+
import { importConsumerDep, resolveConsumerDep } from './resolve-consumer-dep.mjs'
|
|
5
|
+
import { consumerDepFixtureRoot } from './test-fixtures/consumer-dep-fixture-root.mjs'
|
|
6
|
+
|
|
7
|
+
describe('resolveConsumerDep', () => {
|
|
8
|
+
it('resolves vite from consumer node_modules via filesystem resolver', () => {
|
|
9
|
+
const vitePath = resolveConsumerDep('vite', consumerDepFixtureRoot)
|
|
10
|
+
assert.match(vitePath, /node_modules[/\\]vite[/\\]/)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('imports vite without createRequire', async () => {
|
|
14
|
+
const vite = await importConsumerDep('vite', consumerDepFixtureRoot)
|
|
15
|
+
assert.equal(typeof vite.createServer, 'function')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('resolves nested package exports (TanStack router vite subpath)', () => {
|
|
19
|
+
const path = resolveConsumerDep('@tanstack/router-plugin/vite', consumerDepFixtureRoot)
|
|
20
|
+
assert.match(path, /node_modules[/\\]@tanstack[/\\]router-plugin[/\\]/)
|
|
21
|
+
})
|
|
22
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tanstack/router-plugin",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"exports": {
|
|
5
|
+
"./vite": {
|
|
6
|
+
"import": {
|
|
7
|
+
"types": "./dist/esm/vite.d.ts",
|
|
8
|
+
"default": "./dist/esm/vite.js"
|
|
9
|
+
},
|
|
10
|
+
"require": {
|
|
11
|
+
"types": "./dist/cjs/vite.d.cts",
|
|
12
|
+
"default": "./dist/cjs/vite.cjs"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function jsxDEV() {}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function jsx() {}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function createServer() {}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { dirname, resolve } from 'node:path'
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
|
|
4
|
+
/** Portable fake consumer root — no sibling monorepo checkout required. */
|
|
5
|
+
export const consumerDepFixtureRoot = resolve(
|
|
6
|
+
dirname(fileURLToPath(import.meta.url)),
|
|
7
|
+
'consumer-dep',
|
|
8
|
+
)
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { resolve } from 'node:path'
|
|
3
|
-
import { pathToFileURL } from 'node:url'
|
|
1
|
+
import { resolveConsumerDep } from '../resolve-consumer-dep.mjs'
|
|
4
2
|
|
|
5
3
|
/** Subpath runtimes only — do not alias top-level `react` / `react-dom` (breaks `react/jsx-runtime`). */
|
|
6
4
|
const REACT_RUNTIME_SPECS = [
|
|
@@ -15,13 +13,12 @@ const REACT_RUNTIME_SPECS = [
|
|
|
15
13
|
* `node_modules/@woodsportal/hubspot-kit` never loads a mismatched CJS copy in the browser.
|
|
16
14
|
*/
|
|
17
15
|
export function consumerReactAliases(projectRoot) {
|
|
18
|
-
const require = createRequire(pathToFileURL(resolve(projectRoot, 'package.json')))
|
|
19
16
|
/** @type {Record<string, string>} */
|
|
20
17
|
const alias = {}
|
|
21
18
|
|
|
22
19
|
for (const spec of REACT_RUNTIME_SPECS) {
|
|
23
20
|
try {
|
|
24
|
-
alias[spec] =
|
|
21
|
+
alias[spec] = resolveConsumerDep(spec, projectRoot)
|
|
25
22
|
} catch {
|
|
26
23
|
// consumer may omit optional subpaths until install completes
|
|
27
24
|
}
|
|
@@ -1,32 +1,12 @@
|
|
|
1
1
|
import assert from 'node:assert/strict'
|
|
2
|
-
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
|
3
|
-
import { tmpdir } from 'node:os'
|
|
4
|
-
import { join, resolve } from 'node:path'
|
|
5
2
|
import { describe, it } from 'node:test'
|
|
6
3
|
|
|
4
|
+
import { consumerDepFixtureRoot } from '../test-fixtures/consumer-dep-fixture-root.mjs'
|
|
7
5
|
import { consumerReactAliases } from './consumer-react-aliases.js'
|
|
8
6
|
|
|
9
7
|
describe('consumerReactAliases', () => {
|
|
10
8
|
it('resolves react jsx runtimes from the consumer project root', () => {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
mkdirSync(join(root, 'node_modules', 'react'), { recursive: true })
|
|
14
|
-
writeFileSync(
|
|
15
|
-
join(root, 'node_modules', 'react', 'package.json'),
|
|
16
|
-
JSON.stringify({
|
|
17
|
-
name: 'react',
|
|
18
|
-
exports: {
|
|
19
|
-
'./jsx-dev-runtime': './jsx-dev-runtime.js',
|
|
20
|
-
},
|
|
21
|
-
}),
|
|
22
|
-
)
|
|
23
|
-
writeFileSync(join(root, 'node_modules', 'react', 'jsx-dev-runtime.js'), 'export {}')
|
|
24
|
-
writeFileSync(join(root, 'package.json'), JSON.stringify({ name: 'fixture', type: 'module' }))
|
|
25
|
-
|
|
26
|
-
const alias = consumerReactAliases(root)
|
|
27
|
-
assert.match(alias['react/jsx-dev-runtime'], /node_modules[/\\]react[/\\]jsx-dev-runtime\.js$/)
|
|
28
|
-
} finally {
|
|
29
|
-
rmSync(root, { recursive: true, force: true })
|
|
30
|
-
}
|
|
9
|
+
const alias = consumerReactAliases(consumerDepFixtureRoot)
|
|
10
|
+
assert.match(alias['react/jsx-dev-runtime'], /node_modules[/\\]react[/\\]jsx-dev-runtime/)
|
|
31
11
|
})
|
|
32
12
|
})
|
|
@@ -1,15 +1,29 @@
|
|
|
1
|
-
import { createRequire } from 'node:module'
|
|
2
1
|
import { resolve, sep } from 'node:path'
|
|
3
|
-
|
|
2
|
+
|
|
3
|
+
import { resolveConsumerDep } from '../resolve-consumer-dep.mjs'
|
|
4
|
+
|
|
5
|
+
/** Let Vite dedupe + optimizeDeps handle React; forced absolute paths serve CJS in the browser. */
|
|
6
|
+
export const SKIP_CONSUMER_RESOLVE_SPECS = new Set([
|
|
7
|
+
'react',
|
|
8
|
+
'react-dom',
|
|
9
|
+
'react/jsx-runtime',
|
|
10
|
+
'react/jsx-dev-runtime',
|
|
11
|
+
'react-dom/client',
|
|
12
|
+
'react/compiler-runtime',
|
|
13
|
+
])
|
|
14
|
+
|
|
15
|
+
function shouldSkipConsumerResolve(source) {
|
|
16
|
+
if (SKIP_CONSUMER_RESOLVE_SPECS.has(source)) return true
|
|
17
|
+
if (source.startsWith('react/') || source.startsWith('react-dom/')) return true
|
|
18
|
+
return false
|
|
19
|
+
}
|
|
4
20
|
|
|
5
21
|
/**
|
|
6
22
|
* When Vite transforms kit dev UI (`.wphs/`, package src, or dev-module-config),
|
|
7
23
|
* bare imports must resolve from the consumer project's node_modules.
|
|
8
24
|
*/
|
|
9
25
|
export default function resolveFromProjectRoot(projectRoot, kitRoot) {
|
|
10
|
-
const require = createRequire(pathToFileURL(resolve(projectRoot, 'package.json')))
|
|
11
26
|
const kitRootNorm = resolve(kitRoot)
|
|
12
|
-
const wphsNorm = resolve(projectRoot, '.wphs')
|
|
13
27
|
|
|
14
28
|
function isKitUiImporter(importer) {
|
|
15
29
|
const norm = resolve(importer)
|
|
@@ -31,8 +45,11 @@ export default function resolveFromProjectRoot(projectRoot, kitRoot) {
|
|
|
31
45
|
if (!isKitUiImporter(importer)) {
|
|
32
46
|
return null
|
|
33
47
|
}
|
|
48
|
+
if (shouldSkipConsumerResolve(source)) {
|
|
49
|
+
return null
|
|
50
|
+
}
|
|
34
51
|
try {
|
|
35
|
-
return
|
|
52
|
+
return resolveConsumerDep(source, projectRoot)
|
|
36
53
|
} catch {
|
|
37
54
|
return null
|
|
38
55
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { resolve } from 'node:path'
|
|
3
|
+
import { describe, it } from 'node:test'
|
|
4
|
+
|
|
5
|
+
import { consumerDepFixtureRoot } from '../test-fixtures/consumer-dep-fixture-root.mjs'
|
|
6
|
+
import resolveFromProjectRoot, {
|
|
7
|
+
SKIP_CONSUMER_RESOLVE_SPECS,
|
|
8
|
+
} from './resolve-from-project-root.js'
|
|
9
|
+
|
|
10
|
+
describe('resolveFromProjectRoot', () => {
|
|
11
|
+
const kitRoot = resolve(consumerDepFixtureRoot, 'kit')
|
|
12
|
+
const kitUiFile = resolve(consumerDepFixtureRoot, '.wphs/.kit-module-config/ui/ModuleConfigNavContext.tsx')
|
|
13
|
+
const appFile = resolve(consumerDepFixtureRoot, 'src/app.tsx')
|
|
14
|
+
|
|
15
|
+
const plugin = resolveFromProjectRoot(consumerDepFixtureRoot, kitRoot)
|
|
16
|
+
const resolveId = /** @type {(source: string, importer: string) => Promise<string | null>} */ (
|
|
17
|
+
plugin.resolveId.bind(plugin)
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
it('does not force-resolve react for kit UI (avoids CJS index.js in browser)', async () => {
|
|
21
|
+
assert.ok(SKIP_CONSUMER_RESOLVE_SPECS.has('react'))
|
|
22
|
+
assert.equal(await resolveId('react', kitUiFile), null)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('does not force-resolve react/jsx-dev-runtime for kit UI (alias + optimizeDeps)', async () => {
|
|
26
|
+
assert.equal(await resolveId('react/jsx-dev-runtime', kitUiFile), null)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('still resolves other deps from the consumer for kit UI', async () => {
|
|
30
|
+
const resolved = await resolveId('vite', kitUiFile)
|
|
31
|
+
assert.match(resolved ?? '', /node_modules[/\\]vite[/\\]/)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('ignores non-kit importers', async () => {
|
|
35
|
+
assert.equal(await resolveId('vite', appFile), null)
|
|
36
|
+
})
|
|
37
|
+
})
|