@ossy/app 1.39.0 → 1.39.2
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/cli/build.task.js +9 -2
- package/cli/get-platform-files.task.js +57 -18
- package/cli/manifest-plugin.js +18 -4
- package/package.json +9 -9
package/cli/build.task.js
CHANGED
|
@@ -137,13 +137,20 @@ function generateResourceStub ({ stubAbs, sourceAbs }) {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
// Components are re-exported as-is — the manifest plugin reads `metadata.id`
|
|
140
|
-
//
|
|
140
|
+
// and the optional `slot` export from the bundled output. Components don't get
|
|
141
141
|
// route stubs or hydration bootstrapping; they're loaded on-demand at runtime.
|
|
142
|
+
// The `slot` export (when present) identifies the named shell slot this
|
|
143
|
+
// component fills automatically — see PlatformShell for the slot registry.
|
|
144
|
+
// We use `export *` for named exports rather than an explicit list because
|
|
145
|
+
// `slot` is optional: some components don't export it, and a static named
|
|
146
|
+
// re-export of a binding that doesn't exist causes a Rollup MISSING_EXPORT
|
|
147
|
+
// error at build time.
|
|
142
148
|
function generateComponentStub ({ stubAbs, sourceAbs }) {
|
|
143
149
|
const importPath = relImport(stubAbs, sourceAbs)
|
|
144
150
|
return [
|
|
145
151
|
'// Generated by @ossy/app — do not edit',
|
|
146
|
-
`export
|
|
152
|
+
`export * from '${importPath}'`,
|
|
153
|
+
`export { default } from '${importPath}'`,
|
|
147
154
|
'',
|
|
148
155
|
].join('\n')
|
|
149
156
|
}
|
|
@@ -35,7 +35,7 @@ export const LAYOUT_FILE_PATTERN = /\.layout\.(jsx?|tsx?)$/
|
|
|
35
35
|
* @property {PlatformEntry[]} emails Discovered `*.email.{jsx,tsx,...}` entries.
|
|
36
36
|
* @property {PlatformEntry[]} actions Discovered `*.action.{js,mjs,cjs}` entries.
|
|
37
37
|
* @property {PlatformEntry[]} e2es Discovered `*.e2e.{js,mjs,cjs}` entries (installed packages only).
|
|
38
|
-
* @property {PlatformEntry[]} layouts Discovered `*.layout.{jsx,tsx,...}` entries.
|
|
38
|
+
* @property {PlatformEntry[]} layouts Discovered `*.layout.{jsx,tsx,...}` entries (app root `src/` only — never from installed packages).
|
|
39
39
|
*/
|
|
40
40
|
export function discoverFilesByPattern (srcDir, filePattern) {
|
|
41
41
|
const dir = path.resolve(srcDir)
|
|
@@ -104,24 +104,57 @@ export function filePathToRoute (absPath, srcDir) {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
/**
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
107
|
+
* Collects all `node_modules` directories that Node.js would search when
|
|
108
|
+
* resolving imports from `projectRoot`, walking up the filesystem the same
|
|
109
|
+
* way the Node module-resolution algorithm does. This is essential for npm
|
|
110
|
+
* workspaces where packages are hoisted to a root-level `node_modules` that
|
|
111
|
+
* may be several directories above the consuming project.
|
|
112
|
+
*
|
|
113
|
+
* @param {string} projectRoot
|
|
114
|
+
* @returns {string[]} Absolute paths to existing `node_modules` directories,
|
|
115
|
+
* ordered from most-specific (closest to `projectRoot`) to least-specific.
|
|
116
|
+
*/
|
|
117
|
+
function collectNodeModulesDirs (projectRoot) {
|
|
118
|
+
const dirs = []
|
|
119
|
+
let current = projectRoot
|
|
120
|
+
while (true) {
|
|
121
|
+
const nm = path.join(current, 'node_modules')
|
|
122
|
+
if (fs.existsSync(nm) && fs.statSync(nm).isDirectory()) dirs.push(nm)
|
|
123
|
+
const parent = path.dirname(current)
|
|
124
|
+
if (parent === current) break
|
|
125
|
+
current = parent
|
|
126
|
+
}
|
|
127
|
+
return dirs
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Walks `node_modules/` directories reachable from `projectRoot` looking for
|
|
132
|
+
* packages that declare an `"ossy": { "src": "./src" }` field in their
|
|
133
|
+
* `package.json`. Returns entries from those packages' source trees, tagged
|
|
134
|
+
* with `packageName` and `packageSrcDir` so the build pipeline can namespace
|
|
135
|
+
* them correctly.
|
|
111
136
|
*
|
|
112
137
|
* Handles both flat packages (`node_modules/foo`) and scoped packages
|
|
113
|
-
* (`node_modules/@scope/foo`).
|
|
138
|
+
* (`node_modules/@scope/foo`). Walks up the directory tree following Node.js
|
|
139
|
+
* module-resolution order so that monorepo setups with hoisted packages
|
|
140
|
+
* (where dependencies live in a root-level `node_modules` rather than a
|
|
141
|
+
* package-local one) are discovered correctly.
|
|
114
142
|
*
|
|
115
143
|
* @param {string} projectRoot Absolute path to the consuming project root (parent of `src/`).
|
|
116
144
|
* @returns {PlatformEntry[]}
|
|
117
145
|
*/
|
|
118
146
|
export function discoverInstalledPackageEntries (projectRoot) {
|
|
119
|
-
const
|
|
120
|
-
if (
|
|
147
|
+
const nmDirs = collectNodeModulesDirs(projectRoot)
|
|
148
|
+
if (nmDirs.length === 0) return []
|
|
121
149
|
|
|
122
150
|
const entries = []
|
|
151
|
+
const seen = new Set()
|
|
123
152
|
|
|
124
153
|
const tryPackageDir = (pkgDir) => {
|
|
154
|
+
const real = fs.realpathSync(pkgDir)
|
|
155
|
+
if (seen.has(real)) return
|
|
156
|
+
seen.add(real)
|
|
157
|
+
|
|
125
158
|
const pkgJsonPath = path.join(pkgDir, 'package.json')
|
|
126
159
|
if (!fs.existsSync(pkgJsonPath)) return
|
|
127
160
|
let pkg
|
|
@@ -141,7 +174,8 @@ export function discoverInstalledPackageEntries (projectRoot) {
|
|
|
141
174
|
...discoverFilesByPattern(packageSrcDir, EMAIL_FILE_PATTERN),
|
|
142
175
|
...discoverFilesByPattern(packageSrcDir, ACTION_FILE_PATTERN),
|
|
143
176
|
...discoverFilesByPattern(packageSrcDir, E2E_FILE_PATTERN),
|
|
144
|
-
|
|
177
|
+
// Layouts are intentionally excluded: *.layout.jsx is an app-level
|
|
178
|
+
// primitive only. Feature packages must never define layouts.
|
|
145
179
|
]
|
|
146
180
|
for (const sourcePath of files) {
|
|
147
181
|
const stat = fs.statSync(sourcePath)
|
|
@@ -152,17 +186,19 @@ export function discoverInstalledPackageEntries (projectRoot) {
|
|
|
152
186
|
}
|
|
153
187
|
}
|
|
154
188
|
|
|
155
|
-
for (const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
189
|
+
for (const nmDir of nmDirs) {
|
|
190
|
+
for (const entry of fs.readdirSync(nmDir, { withFileTypes: true })) {
|
|
191
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue
|
|
192
|
+
if (entry.name.startsWith('@')) {
|
|
193
|
+
const scopeDir = path.join(nmDir, entry.name)
|
|
194
|
+
for (const scoped of fs.readdirSync(scopeDir, { withFileTypes: true })) {
|
|
195
|
+
if (scoped.isDirectory() || scoped.isSymbolicLink()) {
|
|
196
|
+
tryPackageDir(path.join(scopeDir, scoped.name))
|
|
197
|
+
}
|
|
162
198
|
}
|
|
199
|
+
} else {
|
|
200
|
+
tryPackageDir(path.join(nmDir, entry.name))
|
|
163
201
|
}
|
|
164
|
-
} else {
|
|
165
|
-
tryPackageDir(path.join(nmDir, entry.name))
|
|
166
202
|
}
|
|
167
203
|
}
|
|
168
204
|
|
|
@@ -192,6 +228,9 @@ export default async function getPlatformFiles (srcDir) {
|
|
|
192
228
|
// from the local `src/` — to avoid pulling in per-project duplicates of the
|
|
193
229
|
// same aggregate class or test suite that is canonically owned by the feature
|
|
194
230
|
// package.
|
|
231
|
+
// Layouts are the opposite restriction: they are only discovered from the
|
|
232
|
+
// app's own `src/`, never from installed packages. Feature packages are pure
|
|
233
|
+
// content (pages, actions, tasks, etc.) and must never define layouts.
|
|
195
234
|
const localFiles = [
|
|
196
235
|
...discoverFilesByPattern(srcDir, PAGE_FILE_PATTERN),
|
|
197
236
|
...discoverFilesByPattern(srcDir, API_FILE_PATTERN),
|
package/cli/manifest-plugin.js
CHANGED
|
@@ -39,8 +39,14 @@ import { metadataIdFromFile, defaultPageRoute } from './get-platform-files.task.
|
|
|
39
39
|
* (e.g. `/static/home.page-7f2a.js`).
|
|
40
40
|
*
|
|
41
41
|
* @typedef {object} ComponentManifestEntry
|
|
42
|
-
* @property {string} id
|
|
43
|
-
* @property {string} entry
|
|
42
|
+
* @property {string} id Unique component id (from `metadata.id`).
|
|
43
|
+
* @property {string} entry URL the platform serves the component bundle from.
|
|
44
|
+
* @property {string} [slot] Named shell slot this component fills (from `export const slot`).
|
|
45
|
+
*
|
|
46
|
+
* @typedef {object} SlotManifestEntry
|
|
47
|
+
* @property {string} slot Named shell slot (e.g. `'header'`, `'sidebar'`, `'resource:booking/service/form'`).
|
|
48
|
+
* @property {string} entry URL the platform serves the component bundle from.
|
|
49
|
+
* @property {string} [package] npm package name that contributed this slot (from installed packages).
|
|
44
50
|
*
|
|
45
51
|
* @typedef {object} AggregateManifestEntry
|
|
46
52
|
* @property {string} id AggregateType string (e.g. `'User'`).
|
|
@@ -78,6 +84,7 @@ import { metadataIdFromFile, defaultPageRoute } from './get-platform-files.task.
|
|
|
78
84
|
* @typedef {object} Manifest
|
|
79
85
|
* @property {ManifestEntry[]} entries Flat, ordered list of every routable thing.
|
|
80
86
|
* @property {ComponentManifestEntry[]} components Discovered `*.component.*` entries, keyed by id.
|
|
87
|
+
* @property {SlotManifestEntry[]} slots Components that declare `export const slot` — auto-injected into shell slots.
|
|
81
88
|
* @property {object[]} resourceTemplates Each `*.resource.js` file's default-exported template object.
|
|
82
89
|
* @property {AggregateManifestEntry[]} aggregates Discovered `*.aggregate.js` entries from installed packages.
|
|
83
90
|
* @property {IntegrationManifestEntry[]} integrations Discovered `*.integration.js` entries.
|
|
@@ -150,6 +157,8 @@ export function manifestPlugin ({ entriesByStub, srcDir, staticOutDir, configVal
|
|
|
150
157
|
const seenStartupIds = new Set()
|
|
151
158
|
const seenEmailIds = new Set()
|
|
152
159
|
const seenActionIds = new Set()
|
|
160
|
+
/** @type {SlotManifestEntry[]} */
|
|
161
|
+
const slots = []
|
|
153
162
|
/** @type {E2eManifestEntry[]} */
|
|
154
163
|
const e2es = []
|
|
155
164
|
const seenE2eIds = new Set()
|
|
@@ -399,7 +408,12 @@ export function manifestPlugin ({ entriesByStub, srcDir, staticOutDir, configVal
|
|
|
399
408
|
seenIds[entryInfo.kind].add(id)
|
|
400
409
|
|
|
401
410
|
if (entryInfo.kind === 'component') {
|
|
402
|
-
|
|
411
|
+
const slotName = typeof mod.slot === 'string' && mod.slot.trim() ? mod.slot.trim() : undefined
|
|
412
|
+
components.push({ id, entry: url, ...(slotName ? { slot: slotName } : {}) })
|
|
413
|
+
if (slotName) {
|
|
414
|
+
const packageName = entryInfo.packageName
|
|
415
|
+
slots.push({ slot: slotName, entry: url, ...(packageName ? { package: packageName } : {}) })
|
|
416
|
+
}
|
|
403
417
|
continue
|
|
404
418
|
}
|
|
405
419
|
|
|
@@ -425,7 +439,7 @@ export function manifestPlugin ({ entriesByStub, srcDir, staticOutDir, configVal
|
|
|
425
439
|
}
|
|
426
440
|
|
|
427
441
|
/** @type {Manifest} */
|
|
428
|
-
const manifest = { entries, components, resourceTemplates, aggregates, integrations, startups, emails, actions, e2es, layouts, config: configValue || {} }
|
|
442
|
+
const manifest = { entries, components, slots, resourceTemplates, aggregates, integrations, startups, emails, actions, e2es, layouts, config: configValue || {} }
|
|
429
443
|
fs.mkdirSync(path.dirname(manifestPath), { recursive: true })
|
|
430
444
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), 'utf8')
|
|
431
445
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ossy/app",
|
|
3
|
-
"version": "1.39.
|
|
3
|
+
"version": "1.39.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"source": "./src/index.js",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -38,14 +38,14 @@
|
|
|
38
38
|
"@babel/eslint-parser": "^7.15.8",
|
|
39
39
|
"@babel/preset-react": "^7.26.3",
|
|
40
40
|
"@babel/register": "^7.25.9",
|
|
41
|
-
"@ossy/design-system": "^1.39.
|
|
41
|
+
"@ossy/design-system": "^1.39.2",
|
|
42
42
|
"@ossy/pages": "^1.23.0",
|
|
43
|
-
"@ossy/platform": "^1.38.
|
|
44
|
-
"@ossy/router": "^1.39.
|
|
45
|
-
"@ossy/router-react": "^1.39.
|
|
46
|
-
"@ossy/sdk": "^1.39.
|
|
47
|
-
"@ossy/sdk-react": "^1.39.
|
|
48
|
-
"@ossy/themes": "^1.39.
|
|
43
|
+
"@ossy/platform": "^1.38.2",
|
|
44
|
+
"@ossy/router": "^1.39.2",
|
|
45
|
+
"@ossy/router-react": "^1.39.2",
|
|
46
|
+
"@ossy/sdk": "^1.39.2",
|
|
47
|
+
"@ossy/sdk-react": "^1.39.2",
|
|
48
|
+
"@ossy/themes": "^1.39.2",
|
|
49
49
|
"@rollup/plugin-alias": "^6.0.0",
|
|
50
50
|
"@rollup/plugin-babel": "^7.0.0",
|
|
51
51
|
"@rollup/plugin-commonjs": "^29.0.0",
|
|
@@ -79,5 +79,5 @@
|
|
|
79
79
|
"README.md",
|
|
80
80
|
"tsconfig.json"
|
|
81
81
|
],
|
|
82
|
-
"gitHead": "
|
|
82
|
+
"gitHead": "51e33c2eaff6973c34f00024a082c6912252ec72"
|
|
83
83
|
}
|