@ossy/app 1.28.0 → 1.29.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.
- package/cli/build.task.js +16 -1
- package/cli/get-platform-files.task.js +11 -4
- package/cli/manifest-plugin.js +33 -1
- package/package.json +10 -10
package/cli/build.task.js
CHANGED
|
@@ -15,10 +15,11 @@ import getPlatformFiles, {
|
|
|
15
15
|
TASK_FILE_PATTERN,
|
|
16
16
|
RESOURCE_FILE_PATTERN,
|
|
17
17
|
COMPONENT_FILE_PATTERN,
|
|
18
|
+
AGGREGATE_FILE_PATTERN,
|
|
18
19
|
} from './get-platform-files.task.js'
|
|
19
20
|
import { manifestPlugin } from './manifest-plugin.js'
|
|
20
21
|
|
|
21
|
-
export { PAGE_FILE_PATTERN, API_FILE_PATTERN, TASK_FILE_PATTERN, RESOURCE_FILE_PATTERN, COMPONENT_FILE_PATTERN }
|
|
22
|
+
export { PAGE_FILE_PATTERN, API_FILE_PATTERN, TASK_FILE_PATTERN, RESOURCE_FILE_PATTERN, COMPONENT_FILE_PATTERN, AGGREGATE_FILE_PATTERN }
|
|
22
23
|
|
|
23
24
|
// Generated entry stubs live under `build/.ossy/entries/`. Putting them
|
|
24
25
|
// inside `build/` means they're cleaned automatically with every build,
|
|
@@ -143,11 +144,24 @@ function generateComponentStub ({ stubAbs, sourceAbs }) {
|
|
|
143
144
|
].join('\n')
|
|
144
145
|
}
|
|
145
146
|
|
|
147
|
+
// Aggregates are server-side only. The stub re-exports everything so the
|
|
148
|
+
// manifest plugin can read `Aggregate` (the class) and `id` (the AggregateType
|
|
149
|
+
// string) from the bundled output.
|
|
150
|
+
function generateAggregateStub ({ stubAbs, sourceAbs }) {
|
|
151
|
+
const importPath = relImport(stubAbs, sourceAbs)
|
|
152
|
+
return [
|
|
153
|
+
'// Generated by @ossy/app — do not edit',
|
|
154
|
+
`export * from '${importPath}'`,
|
|
155
|
+
'',
|
|
156
|
+
].join('\n')
|
|
157
|
+
}
|
|
158
|
+
|
|
146
159
|
function stubFor (kind, args) {
|
|
147
160
|
if (kind === 'page') return generatePageStub(args)
|
|
148
161
|
if (kind === 'api') return generateApiStub(args)
|
|
149
162
|
if (kind === 'resource') return generateResourceStub(args)
|
|
150
163
|
if (kind === 'component') return generateComponentStub(args)
|
|
164
|
+
if (kind === 'aggregate') return generateAggregateStub(args)
|
|
151
165
|
return generateTaskStub(args)
|
|
152
166
|
}
|
|
153
167
|
|
|
@@ -189,6 +203,7 @@ export async function build (cliArgs = []) {
|
|
|
189
203
|
...platformFiles.tasks,
|
|
190
204
|
...platformFiles.resources,
|
|
191
205
|
...platformFiles.components,
|
|
206
|
+
...platformFiles.aggregates,
|
|
192
207
|
]
|
|
193
208
|
|
|
194
209
|
const entriesByStub = new Map()
|
|
@@ -6,9 +6,10 @@ export const API_FILE_PATTERN = /\.api\.(mjs|cjs|js)$/
|
|
|
6
6
|
export const TASK_FILE_PATTERN = /\.task\.(mjs|cjs|js)$/
|
|
7
7
|
export const RESOURCE_FILE_PATTERN = /\.resource\.(mjs|cjs|js)$/
|
|
8
8
|
export const COMPONENT_FILE_PATTERN = /\.component\.(jsx?|tsx?)$/
|
|
9
|
+
export const AGGREGATE_FILE_PATTERN = /\.aggregate\.(mjs|cjs|js)$/
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
|
-
* @typedef {'page' | 'api' | 'task' | 'resource' | 'component'} EntryKind
|
|
12
|
+
* @typedef {'page' | 'api' | 'task' | 'resource' | 'component' | 'aggregate'} EntryKind
|
|
12
13
|
*
|
|
13
14
|
* @typedef {object} PlatformEntry
|
|
14
15
|
* @property {EntryKind} kind Which bucket this entry lives in.
|
|
@@ -22,6 +23,7 @@ export const COMPONENT_FILE_PATTERN = /\.component\.(jsx?|tsx?)$/
|
|
|
22
23
|
* @property {PlatformEntry[]} tasks Discovered `*.task.{js,mjs,cjs}` entries.
|
|
23
24
|
* @property {PlatformEntry[]} resources Discovered `*.resource.{js,mjs,cjs}` entries.
|
|
24
25
|
* @property {PlatformEntry[]} components Discovered `*.component.{jsx,tsx,...}` entries.
|
|
26
|
+
* @property {PlatformEntry[]} aggregates Discovered `*.aggregate.{js,mjs,cjs}` entries (installed packages only).
|
|
25
27
|
*/
|
|
26
28
|
export function discoverFilesByPattern (srcDir, filePattern) {
|
|
27
29
|
const dir = path.resolve(srcDir)
|
|
@@ -45,6 +47,7 @@ function classifyFile (absPath) {
|
|
|
45
47
|
if (TASK_FILE_PATTERN.test(base)) return 'task'
|
|
46
48
|
if (RESOURCE_FILE_PATTERN.test(base)) return 'resource'
|
|
47
49
|
if (COMPONENT_FILE_PATTERN.test(base)) return 'component'
|
|
50
|
+
if (AGGREGATE_FILE_PATTERN.test(base)) return 'aggregate'
|
|
48
51
|
return null
|
|
49
52
|
}
|
|
50
53
|
|
|
@@ -114,6 +117,7 @@ export function discoverInstalledPackageEntries (projectRoot) {
|
|
|
114
117
|
...discoverFilesByPattern(packageSrcDir, TASK_FILE_PATTERN),
|
|
115
118
|
...discoverFilesByPattern(packageSrcDir, RESOURCE_FILE_PATTERN),
|
|
116
119
|
...discoverFilesByPattern(packageSrcDir, COMPONENT_FILE_PATTERN),
|
|
120
|
+
...discoverFilesByPattern(packageSrcDir, AGGREGATE_FILE_PATTERN),
|
|
117
121
|
]
|
|
118
122
|
for (const sourcePath of files) {
|
|
119
123
|
const stat = fs.statSync(sourcePath)
|
|
@@ -157,9 +161,12 @@ export function discoverInstalledPackageEntries (projectRoot) {
|
|
|
157
161
|
* @returns {Promise<PlatformFiles>}
|
|
158
162
|
*/
|
|
159
163
|
export default async function getPlatformFiles (srcDir) {
|
|
160
|
-
const out = { pages: [], apis: [], tasks: [], resources: [], components: [] }
|
|
161
|
-
const bucketByKind = { page: 'pages', api: 'apis', task: 'tasks', resource: 'resources', component: 'components' }
|
|
164
|
+
const out = { pages: [], apis: [], tasks: [], resources: [], components: [], aggregates: [] }
|
|
165
|
+
const bucketByKind = { page: 'pages', api: 'apis', task: 'tasks', resource: 'resources', component: 'components', aggregate: 'aggregates' }
|
|
162
166
|
|
|
167
|
+
// Aggregates are only discovered from installed packages — never from the
|
|
168
|
+
// local `src/` — to avoid pulling in per-project duplicates of the same
|
|
169
|
+
// aggregate class that is canonically owned by the feature package.
|
|
163
170
|
const localFiles = [
|
|
164
171
|
...discoverFilesByPattern(srcDir, PAGE_FILE_PATTERN),
|
|
165
172
|
...discoverFilesByPattern(srcDir, API_FILE_PATTERN),
|
|
@@ -171,7 +178,7 @@ export default async function getPlatformFiles (srcDir) {
|
|
|
171
178
|
const stat = fs.statSync(sourcePath)
|
|
172
179
|
if (!stat.isFile() || stat.size === 0) continue
|
|
173
180
|
const kind = classifyFile(sourcePath)
|
|
174
|
-
if (!kind) continue
|
|
181
|
+
if (!kind || kind === 'aggregate') continue
|
|
175
182
|
out[bucketByKind[kind]].push({ kind, sourcePath })
|
|
176
183
|
}
|
|
177
184
|
|
package/cli/manifest-plugin.js
CHANGED
|
@@ -42,10 +42,15 @@ import { metadataIdFromFile, defaultPageRoute } from './get-platform-files.task.
|
|
|
42
42
|
* @property {string} id Unique component id (from `metadata.id`).
|
|
43
43
|
* @property {string} entry URL the platform serves the component bundle from.
|
|
44
44
|
*
|
|
45
|
+
* @typedef {object} AggregateManifestEntry
|
|
46
|
+
* @property {string} id AggregateType string (e.g. `'User'`).
|
|
47
|
+
* @property {string} entry URL the platform serves the aggregate bundle from.
|
|
48
|
+
*
|
|
45
49
|
* @typedef {object} Manifest
|
|
46
50
|
* @property {ManifestEntry[]} entries Flat, ordered list of every routable thing.
|
|
47
51
|
* @property {ComponentManifestEntry[]} components Discovered `*.component.*` entries, keyed by id.
|
|
48
52
|
* @property {object[]} resourceTemplates Each `*.resource.js` file's default-exported template object.
|
|
53
|
+
* @property {AggregateManifestEntry[]} aggregates Discovered `*.aggregate.js` entries from installed packages.
|
|
49
54
|
* @property {object} config Inlined `src/config.js` default export.
|
|
50
55
|
*/
|
|
51
56
|
|
|
@@ -93,8 +98,11 @@ export function manifestPlugin ({ entriesByStub, srcDir, staticOutDir, configVal
|
|
|
93
98
|
const resourceTemplates = []
|
|
94
99
|
/** @type {import('./get-platform-files.task.js').ComponentManifestEntry[]} */
|
|
95
100
|
const components = []
|
|
101
|
+
/** @type {AggregateManifestEntry[]} */
|
|
102
|
+
const aggregates = []
|
|
96
103
|
const seenIds = { page: new Set(), api: new Set(), task: new Set(), component: new Set() }
|
|
97
104
|
const seenResourceIds = new Set()
|
|
105
|
+
const seenAggregateIds = new Set()
|
|
98
106
|
|
|
99
107
|
for (const fileName of Object.keys(bundle)) {
|
|
100
108
|
const chunk = bundle[fileName]
|
|
@@ -120,6 +128,30 @@ export function manifestPlugin ({ entriesByStub, srcDir, staticOutDir, configVal
|
|
|
120
128
|
)
|
|
121
129
|
}
|
|
122
130
|
|
|
131
|
+
// Aggregates are server-side classes — the bundle exports `Aggregate`
|
|
132
|
+
// (the class) and `id` (the AggregateType string). They don't produce
|
|
133
|
+
// routable entries; they accumulate into a separate `aggregates` array.
|
|
134
|
+
if (entryInfo.kind === 'aggregate') {
|
|
135
|
+
const AggregateClass = mod && mod.Aggregate
|
|
136
|
+
const aggregateId = mod && mod.id
|
|
137
|
+
if (typeof AggregateClass !== 'function') {
|
|
138
|
+
this.error(
|
|
139
|
+
`[@ossy/app][build] aggregate entry ${entryInfo.sourcePath} must export a class as "Aggregate"`,
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
if (typeof aggregateId !== 'string' || aggregateId.trim() === '') {
|
|
143
|
+
this.error(
|
|
144
|
+
`[@ossy/app][build] aggregate entry ${entryInfo.sourcePath} must export a non-empty string "id"`,
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
if (seenAggregateIds.has(aggregateId)) {
|
|
148
|
+
this.error(`[@ossy/app][build] Duplicate aggregate id "${aggregateId}"`)
|
|
149
|
+
}
|
|
150
|
+
seenAggregateIds.add(aggregateId)
|
|
151
|
+
aggregates.push({ id: aggregateId, entry: url })
|
|
152
|
+
continue
|
|
153
|
+
}
|
|
154
|
+
|
|
123
155
|
// Resources are pure data — the default export *is* the template.
|
|
124
156
|
// They don't use `metadata`, don't need a derived id, and don't
|
|
125
157
|
// produce a routable manifest entry; they accumulate into a separate
|
|
@@ -181,7 +213,7 @@ export function manifestPlugin ({ entriesByStub, srcDir, staticOutDir, configVal
|
|
|
181
213
|
}
|
|
182
214
|
|
|
183
215
|
/** @type {Manifest} */
|
|
184
|
-
const manifest = { entries, components, resourceTemplates, config: configValue || {} }
|
|
216
|
+
const manifest = { entries, components, resourceTemplates, aggregates, config: configValue || {} }
|
|
185
217
|
fs.mkdirSync(path.dirname(manifestPath), { recursive: true })
|
|
186
218
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), 'utf8')
|
|
187
219
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ossy/app",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.29.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"source": "./src/index.js",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -37,15 +37,15 @@
|
|
|
37
37
|
"@babel/eslint-parser": "^7.15.8",
|
|
38
38
|
"@babel/preset-react": "^7.26.3",
|
|
39
39
|
"@babel/register": "^7.25.9",
|
|
40
|
-
"@ossy/connected-components": "^1.
|
|
41
|
-
"@ossy/design-system": "^1.
|
|
40
|
+
"@ossy/connected-components": "^1.29.0",
|
|
41
|
+
"@ossy/design-system": "^1.29.0",
|
|
42
42
|
"@ossy/pages": "^1.23.0",
|
|
43
|
-
"@ossy/platform": "^1.
|
|
44
|
-
"@ossy/router": "^1.
|
|
45
|
-
"@ossy/router-react": "^1.
|
|
46
|
-
"@ossy/sdk": "^1.
|
|
47
|
-
"@ossy/sdk-react": "^1.
|
|
48
|
-
"@ossy/themes": "^1.
|
|
43
|
+
"@ossy/platform": "^1.28.0",
|
|
44
|
+
"@ossy/router": "^1.29.0",
|
|
45
|
+
"@ossy/router-react": "^1.29.0",
|
|
46
|
+
"@ossy/sdk": "^1.29.0",
|
|
47
|
+
"@ossy/sdk-react": "^1.29.0",
|
|
48
|
+
"@ossy/themes": "^1.29.0",
|
|
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": "c71b23b5721bfd12dec0dfb4b50d945b65314898"
|
|
83
83
|
}
|