Skip to content

Commit 50b5613

Browse files
committed
Merge branch 'post-milestone3m' into fix/login-403-logged-in
2 parents fbe3371 + e710b1b commit 50b5613

52 files changed

Lines changed: 4208 additions & 41 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -322,9 +322,13 @@ import { SignupButton } from 'solid-ui/components/signup-button'
322322

323323
Web components use a two-stage build to produce a clean public runtime layout while keeping internal TypeScript artifacts separate:
324324

325-
1. **webpack** (`npm run build-dist`) bundles each component entrypoint and emits the runtime files to `dist/components/<name>/index.js` and `dist/components/<name>/index.esm.js`.
326-
2. **tsc** (`npm run build-js`) emits internal declaration and JS artifacts mirroring the source tree under `dist/v2/components/<name>/`.
327-
3. **`scripts/build-component-dts.mjs`** (runs automatically after tsc as part of `postbuild-js`) writes thin public declaration wrappers at `dist/components/<name>/index.d.ts`, re-exporting from the internal `dist/v2/components/<name>/` output.
325+
1. **`scripts/component-manifest.mjs`** is the source of truth for v2 web components. It defines the component entrypoints used by webpack and the public subpath names exposed from the package.
326+
2. **webpack** (`npm run build-dist`) bundles each component entrypoint from the manifest and emits the runtime files to `dist/components/<name>/index.js` and `dist/components/<name>/index.esm.js`.
327+
3. **tsc** (`npm run build-js`) emits internal declaration and JS artifacts mirroring the source tree under `dist/v2/components/<name>/`.
328+
4. **`scripts/build-component-dts.mjs`** (runs automatically after tsc as part of `postbuild-js`) writes thin public declaration wrappers at `dist/components/<name>/index.d.ts`, re-exporting from the internal `dist/v2/components/<name>/` output.
329+
5. **`scripts/sync-component-exports.mjs`** keeps the `package.json` `exports` map aligned with the manifest. It runs automatically as part of `npm run build` and `npm version` workflows.
330+
331+
The legacy main bundle remains a special case. In [webpack.config.mjs](webpack.config.mjs) only the `main` entry keeps the UMD `UI` global export; component entries are generated from the manifest and built as standalone scripts so they do not clobber one another when loaded directly.
328332

329333
This keeps the `package.json` subpath export fully aligned while exposing only the public `dist/components/...` layout:
330334

@@ -338,6 +342,16 @@ This keeps the `package.json` subpath export fully aligned while exposing only t
338342

339343
Consumers never import from `dist/v2/components/...`; that path is an internal build artifact only.
340344

345+
### Adding a new web component
346+
347+
When adding a new v2 component:
348+
349+
1. Create the component folder under `src/v2/components/<ComponentName>/` with its `index.ts` entrypoint.
350+
2. Add one record to `scripts/component-manifest.mjs`.
351+
3. Run `npm run sync-component-exports` if you want to update `package.json` immediately, or just run `npm run build` and let the build do it automatically.
352+
353+
You should not need to hand-edit the webpack component entry list or the `package.json` component export map anymore.
354+
341355
## Development
342356

343357
When developing a component in solid-ui you can test it in isolation using storybook
@@ -411,4 +425,8 @@ You are logged in as nameOfLoggedIn user.
411425

412426
* Raptor mini: add a readme to the Footer component with example.
413427

414-
* Claude Sonnet 4.6: Make the dop down as a list under the input field and entlarge the pop up, make it higher, adjustable to fit the drop down. And make the drop down arrow area larger
428+
* Claude Sonnet 4.6: Make the dop down as a list under the input field and entlarge the pop up, make it higher, adjustable to fit the drop down. And make the drop down arrow area larger
429+
430+
* GPT-5.4 Model: can you wire up the keyboard interactions and aria attributes for Select.
431+
432+
* GPT-5.4 Model: Take the code from /Users/sharon/2025Dev/solid-ui/src/media/media-capture.ts and make it a web component. Make it work in forms as well as not. Make it configurable and follow LoginButton.

package.json

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,85 @@
1515
"import": "./dist/components/header/index.esm.js",
1616
"require": "./dist/components/header/index.js"
1717
},
18+
"./components/layout/header": {
19+
"types": "./dist/components/header/index.d.ts",
20+
"import": "./dist/components/header/index.esm.js",
21+
"require": "./dist/components/header/index.js"
22+
},
23+
"./components/loginButton": {
24+
"types": "./dist/components/loginButton/index.d.ts",
25+
"import": "./dist/components/loginButton/index.esm.js",
26+
"require": "./dist/components/loginButton/index.js"
27+
},
1828
"./components/login-button": {
1929
"types": "./dist/components/loginButton/index.d.ts",
2030
"import": "./dist/components/loginButton/index.esm.js",
2131
"require": "./dist/components/loginButton/index.js"
2232
},
33+
"./components/auth/login-button": {
34+
"types": "./dist/components/loginButton/index.d.ts",
35+
"import": "./dist/components/loginButton/index.esm.js",
36+
"require": "./dist/components/loginButton/index.js"
37+
},
38+
"./components/auth/signup-button": {
39+
"types": "./dist/components/signupButton/index.d.ts",
40+
"import": "./dist/components/signupButton/index.esm.js",
41+
"require": "./dist/components/signupButton/index.js"
42+
},
2343
"./components/signup-button": {
2444
"types": "./dist/components/signupButton/index.d.ts",
2545
"import": "./dist/components/signupButton/index.esm.js",
2646
"require": "./dist/components/signupButton/index.js"
2747
},
48+
"./components/media/photo-capture": {
49+
"types": "./dist/components/photoCapture/index.d.ts",
50+
"import": "./dist/components/photoCapture/index.esm.js",
51+
"require": "./dist/components/photoCapture/index.js"
52+
},
53+
"./components/photo-capture": {
54+
"types": "./dist/components/photoCapture/index.d.ts",
55+
"import": "./dist/components/photoCapture/index.esm.js",
56+
"require": "./dist/components/photoCapture/index.js"
57+
},
58+
"./components/actions/button": {
59+
"types": "./dist/components/button/index.d.ts",
60+
"import": "./dist/components/button/index.esm.js",
61+
"require": "./dist/components/button/index.js"
62+
},
63+
"./components/button": {
64+
"types": "./dist/components/button/index.d.ts",
65+
"import": "./dist/components/button/index.esm.js",
66+
"require": "./dist/components/button/index.js"
67+
},
2868
"./components/footer": {
2969
"types": "./dist/components/footer/index.d.ts",
3070
"import": "./dist/components/footer/index.esm.js",
3171
"require": "./dist/components/footer/index.js"
72+
},
73+
"./components/layout/footer": {
74+
"types": "./dist/components/footer/index.d.ts",
75+
"import": "./dist/components/footer/index.esm.js",
76+
"require": "./dist/components/footer/index.js"
77+
},
78+
"./components/forms/select": {
79+
"types": "./dist/components/select/index.d.ts",
80+
"import": "./dist/components/select/index.esm.js",
81+
"require": "./dist/components/select/index.js"
82+
},
83+
"./components/select": {
84+
"types": "./dist/components/select/index.d.ts",
85+
"import": "./dist/components/select/index.esm.js",
86+
"require": "./dist/components/select/index.js"
87+
},
88+
"./components/forms/combobox": {
89+
"types": "./dist/components/combobox/index.d.ts",
90+
"import": "./dist/components/combobox/index.esm.js",
91+
"require": "./dist/components/combobox/index.js"
92+
},
93+
"./components/combobox": {
94+
"types": "./dist/components/combobox/index.d.ts",
95+
"import": "./dist/components/combobox/index.esm.js",
96+
"require": "./dist/components/combobox/index.js"
3297
}
3398
},
3499
"files": [
@@ -38,10 +103,11 @@
38103
],
39104
"scripts": {
40105
"clean": "rm -rf ./dist ./src/versionInfo.ts ./docs/api .tsbuildinfo",
41-
"build": "npm run clean && npm run typecheck && npm run build-version && npm run build-dist && npm run build-js && npm run postbuild-js && npm run build-storybook",
106+
"build": "npm run clean && npm run sync-component-exports && npm run typecheck && npm run build-version && npm run build-dist && npm run build-js && npm run postbuild-js && npm run build-storybook",
42107
"build-version": "sh ./timestamp.sh > src/versionInfo.ts && eslint 'src/versionInfo.ts' --fix",
43108
"prebuild-js": "rm -f .tsbuildinfo",
44109
"build-js": "tsc",
110+
"sync-component-exports": "node scripts/sync-component-exports.mjs",
45111
"postbuild-js": "rm -f dist/versionInfo.d.ts dist/versionInfo.d.ts.map && node scripts/build-component-dts.mjs",
46112
"build-dist": "webpack --progress",
47113
"build-form-examples": "npm run build-js && npm run build-version && npm run build-dist && cp ./dist/solid-ui.js ./docs/form-examples/",
@@ -54,12 +120,13 @@
54120
"test-debug": "node --inspect-brk ./node_modules/jest/bin/jest.js --runInBand --watch",
55121
"watch:js": "tsc --watch --preserveWatchOutput",
56122
"watch:component-dts": "node scripts/watch-component-dts.mjs",
123+
"watch:component-exports": "node scripts/watch-component-exports.mjs",
57124
"watch:dist": "webpack --watch --mode development",
58-
"watch": "npm run build-version && npm run build-js && npm run postbuild-js && sh -c 'npm run watch:js & npm run watch:component-dts & npm run watch:dist & wait'",
59-
"dev": "npm run build-version && sh -c 'npm run watch:js & npm run watch:component-dts & npm run watch:dist & wait'",
125+
"watch": "npm run sync-component-exports && npm run build-version && npm run build-js && npm run postbuild-js && sh -c 'npm run watch:js & npm run watch:component-dts & npm run watch:component-exports & npm run watch:dist & wait'",
126+
"dev": "npm run sync-component-exports && npm run build-version && sh -c 'npm run watch:js & npm run watch:component-dts & npm run watch:component-exports & npm run watch:dist & wait'",
60127
"doc": "typedoc --out ./docs/api/ ./src/ --excludeInternal",
61128
"prepublishOnly": "npm run build && npm run lint && npm test && npm run doc",
62-
"preversion": "npm run lint && npm run typecheck && npm test",
129+
"preversion": "npm run sync-component-exports && npm run lint && npm run typecheck && npm test",
63130
"postpublish": "git push origin main --follow-tags",
64131
"storybook": "storybook dev -p 6006",
65132
"build-storybook": "storybook build --output-dir ./examples/storybook"

scripts/build-component-dts.mjs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { existsSync, mkdirSync, readdirSync, statSync, writeFileSync } from 'fs'
22
import path from 'path'
3+
import { v2Components } from './component-manifest.mjs'
34

45
const distDir = path.resolve(process.cwd(), 'dist')
56
const v2ComponentsDir = path.join(distDir, 'v2', 'components')
@@ -9,21 +10,31 @@ if (!existsSync(v2ComponentsDir)) {
910
throw new Error(`Missing expected directory: ${v2ComponentsDir}`)
1011
}
1112

12-
const componentDirs = readdirSync(v2ComponentsDir).filter(name => {
13+
const manifestComponents = v2Components.map(({ sourceDir, sourcePath = sourceDir }) => ({
14+
publicDir: sourceDir,
15+
sourcePath
16+
}))
17+
18+
const fallbackComponentDirs = readdirSync(v2ComponentsDir).filter(name => {
1319
const fullPath = path.join(v2ComponentsDir, name)
1420
return statSync(fullPath).isDirectory()
15-
})
21+
}).map(name => ({
22+
publicDir: name,
23+
sourcePath: name
24+
}))
25+
26+
const componentDirs = manifestComponents.length > 0 ? manifestComponents : fallbackComponentDirs
1627

17-
for (const componentDir of componentDirs) {
18-
const sourceIndex = path.join(v2ComponentsDir, componentDir, 'index.d.ts')
28+
for (const { publicDir, sourcePath } of componentDirs) {
29+
const sourceIndex = path.join(v2ComponentsDir, sourcePath, 'index.d.ts')
1930
if (!existsSync(sourceIndex)) {
2031
continue
2132
}
2233

23-
const outputDir = path.join(publicComponentsDir, componentDir)
34+
const outputDir = path.join(publicComponentsDir, publicDir)
2435
mkdirSync(outputDir, { recursive: true })
2536

26-
const relativePath = path.relative(outputDir, path.join(v2ComponentsDir, componentDir, 'index.d.ts'))
37+
const relativePath = path.relative(outputDir, sourceIndex)
2738
.replace(/\\/g, '/')
2839
.replace(/\.d\.ts$/, '')
2940

scripts/component-manifest.mjs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
export const v2Components = [
2+
{
3+
sourceDir: 'header',
4+
sourcePath: 'layout/header',
5+
exportNames: ['header', 'layout/header']
6+
},
7+
{
8+
sourceDir: 'loginButton',
9+
sourcePath: 'auth/loginButton',
10+
exportNames: ['loginButton', 'login-button', 'auth/login-button']
11+
},
12+
{
13+
sourceDir: 'signupButton',
14+
sourcePath: 'auth/signupButton',
15+
exportNames: ['auth/signup-button', 'signup-button']
16+
},
17+
{
18+
sourceDir: 'photoCapture',
19+
sourcePath: 'media/photoCapture',
20+
exportNames: ['media/photo-capture', 'photo-capture']
21+
},
22+
{
23+
sourceDir: 'button',
24+
sourcePath: 'actions/button',
25+
exportNames: ['actions/button', 'button']
26+
},
27+
{
28+
sourceDir: 'footer',
29+
sourcePath: 'layout/footer',
30+
exportNames: ['footer', 'layout/footer']
31+
},
32+
{
33+
sourceDir: 'select',
34+
sourcePath: 'forms/select',
35+
exportNames: ['forms/select', 'select']
36+
},
37+
{
38+
sourceDir: 'combobox',
39+
sourcePath: 'forms/combobox',
40+
exportNames: ['forms/combobox', 'combobox']
41+
}
42+
]
43+
44+
export const componentEntries = Object.fromEntries(
45+
v2Components.map(({ sourceDir, sourcePath = sourceDir }) => [
46+
sourceDir,
47+
{
48+
import: `./src/v2/components/${sourcePath}/index.ts`
49+
}
50+
])
51+
)
52+
53+
export const componentExports = Object.fromEntries(
54+
v2Components.flatMap(({ sourceDir, exportNames }) =>
55+
exportNames.map(exportName => [
56+
`./components/${exportName}`,
57+
{
58+
types: `./dist/components/${sourceDir}/index.d.ts`,
59+
import: `./dist/components/${sourceDir}/index.esm.js`,
60+
require: `./dist/components/${sourceDir}/index.js`
61+
}
62+
])
63+
)
64+
)

scripts/sync-component-exports.mjs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { readFileSync, writeFileSync } from 'fs'
2+
import path from 'path'
3+
import { componentExports } from './component-manifest.mjs'
4+
5+
const packageJsonPath = path.resolve(process.cwd(), 'package.json')
6+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'))
7+
8+
const preservedExports = Object.fromEntries(
9+
Object.entries(packageJson.exports || {}).filter(([subpath]) => !subpath.startsWith('./components/'))
10+
)
11+
12+
packageJson.exports = {
13+
...preservedExports,
14+
...componentExports
15+
}
16+
17+
writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { spawn } from 'child_process'
2+
import { existsSync, watch } from 'fs'
3+
import path from 'path'
4+
5+
const scriptsDir = path.resolve(process.cwd(), 'scripts')
6+
const manifestFile = 'component-manifest.mjs'
7+
const syncScript = path.resolve(scriptsDir, 'sync-component-exports.mjs')
8+
let syncTimer = null
9+
let running = false
10+
let rerunRequested = false
11+
12+
const runSync = () => {
13+
if (running) {
14+
rerunRequested = true
15+
return
16+
}
17+
18+
running = true
19+
rerunRequested = false
20+
21+
const child = spawn(process.execPath, [syncScript], {
22+
stdio: 'inherit'
23+
})
24+
25+
child.on('exit', code => {
26+
running = false
27+
28+
if (code !== 0) {
29+
console.error(`sync-component-exports exited with code ${code}`)
30+
}
31+
32+
if (rerunRequested) {
33+
runSync()
34+
}
35+
})
36+
}
37+
38+
const scheduleSync = () => {
39+
clearTimeout(syncTimer)
40+
syncTimer = setTimeout(() => {
41+
runSync()
42+
}, 150)
43+
}
44+
45+
if (!existsSync(scriptsDir)) {
46+
throw new Error(`Missing expected directory: ${scriptsDir}`)
47+
}
48+
49+
console.log(`Watching ${path.join(scriptsDir, manifestFile)} for export manifest changes...`)
50+
51+
runSync()
52+
53+
watch(scriptsDir, (eventType, filename) => {
54+
if (!filename) return
55+
if (filename === manifestFile) {
56+
scheduleSync()
57+
}
58+
})

src/login/login.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,10 +1079,20 @@ export function newAppInstance (
10791079
* and/or a developer
10801080
*/
10811081
export async function getUserRoles (): Promise<Array<NamedNode>> {
1082+
const sessionInfo = authSession.info
1083+
if (!sessionInfo?.isLoggedIn || !sessionInfo?.webId) {
1084+
return []
1085+
}
1086+
1087+
const currentUser = authn.currentUser()
1088+
if (!currentUser) {
1089+
return []
1090+
}
1091+
10821092
try {
1083-
const { me, preferencesFile, preferencesFileError } = await ensureLoadedPreferences({})
1093+
const { me, preferencesFile, preferencesFileError } = await ensureLoadedPreferences({ me: currentUser })
10841094
if (!preferencesFile || preferencesFileError) {
1085-
throw new Error(preferencesFileError)
1095+
throw new Error(preferencesFileError || 'Unable to load user preferences file.')
10861096
}
10871097
return solidLogicSingleton.store.each(
10881098
me,

0 commit comments

Comments
 (0)