@ossy/app 1.16.5 → 1.16.7

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 CHANGED
@@ -13,10 +13,11 @@ import getPlatformFiles, {
13
13
  PAGE_FILE_PATTERN,
14
14
  API_FILE_PATTERN,
15
15
  TASK_FILE_PATTERN,
16
+ RESOURCE_FILE_PATTERN,
16
17
  } from './get-platform-files.task.js'
17
18
  import { manifestPlugin } from './manifest-plugin.js'
18
19
 
19
- export { PAGE_FILE_PATTERN, API_FILE_PATTERN, TASK_FILE_PATTERN }
20
+ export { PAGE_FILE_PATTERN, API_FILE_PATTERN, TASK_FILE_PATTERN, RESOURCE_FILE_PATTERN }
20
21
 
21
22
  // Generated entry stubs live under `build/.ossy/entries/`. Putting them
22
23
  // inside `build/` means they're cleaned automatically with every build,
@@ -113,9 +114,22 @@ function generateTaskStub ({ stubAbs, sourceAbs }) {
113
114
  ].join('\n')
114
115
  }
115
116
 
117
+ // Resources are pure data (POJOs default-exported from `*.resource.js`).
118
+ // The stub only pass-through re-exports the default so the manifest plugin
119
+ // can read it via the same `await import(chunkUrl)` path as the other kinds.
120
+ function generateResourceStub ({ stubAbs, sourceAbs }) {
121
+ const importPath = relImport(stubAbs, sourceAbs)
122
+ return [
123
+ '// Generated by @ossy/app — do not edit',
124
+ `export { default } from '${importPath}'`,
125
+ '',
126
+ ].join('\n')
127
+ }
128
+
116
129
  function stubFor (kind, args) {
117
130
  if (kind === 'page') return generatePageStub(args)
118
131
  if (kind === 'api') return generateApiStub(args)
132
+ if (kind === 'resource') return generateResourceStub(args)
119
133
  return generateTaskStub(args)
120
134
  }
121
135
 
@@ -151,7 +165,12 @@ export async function build (cliArgs = []) {
151
165
  ensureDir(stubDir)
152
166
 
153
167
  const platformFiles = await getPlatformFiles(srcDir)
154
- const allEntries = [...platformFiles.pages, ...platformFiles.apis, ...platformFiles.tasks]
168
+ const allEntries = [
169
+ ...platformFiles.pages,
170
+ ...platformFiles.apis,
171
+ ...platformFiles.tasks,
172
+ ...platformFiles.resources,
173
+ ]
155
174
 
156
175
  const entriesByStub = new Map()
157
176
  const stubInputMap = {}
@@ -4,21 +4,22 @@ import path from 'node:path'
4
4
  export const PAGE_FILE_PATTERN = /\.page\.(jsx?|tsx?)$/
5
5
  export const API_FILE_PATTERN = /\.api\.(mjs|cjs|js)$/
6
6
  export const TASK_FILE_PATTERN = /\.task\.(mjs|cjs|js)$/
7
+ export const RESOURCE_FILE_PATTERN = /\.resource\.(mjs|cjs|js)$/
7
8
 
8
9
  /**
9
- * @typedef {'page' | 'api' | 'task'} EntryKind
10
+ * @typedef {'page' | 'api' | 'task' | 'resource'} EntryKind
10
11
  *
11
12
  * @typedef {object} PlatformEntry
12
13
  * @property {EntryKind} kind Which bucket this entry lives in.
13
14
  * @property {string} sourcePath Absolute path to the user-authored source file.
14
15
  *
15
16
  * @typedef {object} PlatformFiles
16
- * @property {PlatformEntry[]} pages Discovered `*.page.{jsx,tsx,...}` entries.
17
- * @property {PlatformEntry[]} apis Discovered `*.api.{js,mjs,cjs}` entries.
18
- * @property {PlatformEntry[]} tasks Discovered `*.task.{js,mjs,cjs}` entries.
17
+ * @property {PlatformEntry[]} pages Discovered `*.page.{jsx,tsx,...}` entries.
18
+ * @property {PlatformEntry[]} apis Discovered `*.api.{js,mjs,cjs}` entries.
19
+ * @property {PlatformEntry[]} tasks Discovered `*.task.{js,mjs,cjs}` entries.
20
+ * @property {PlatformEntry[]} resources Discovered `*.resource.{js,mjs,cjs}` entries.
19
21
  */
20
-
21
- function discoverFilesByPattern (srcDir, filePattern) {
22
+ export function discoverFilesByPattern (srcDir, filePattern) {
22
23
  const dir = path.resolve(srcDir)
23
24
  if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) return []
24
25
  const files = []
@@ -38,6 +39,7 @@ function classifyFile (absPath) {
38
39
  if (PAGE_FILE_PATTERN.test(base)) return 'page'
39
40
  if (API_FILE_PATTERN.test(base)) return 'api'
40
41
  if (TASK_FILE_PATTERN.test(base)) return 'task'
42
+ if (RESOURCE_FILE_PATTERN.test(base)) return 'resource'
41
43
  return null
42
44
  }
43
45
 
@@ -66,6 +68,14 @@ export function defaultPageRoute (id) {
66
68
  return id === 'home' ? '/' : '/' + id
67
69
  }
68
70
 
71
+ /**
72
+ * @returns {{ id: string, path: string }}
73
+ */
74
+ export function filePathToRoute (absPath, srcDir) {
75
+ const id = metadataIdFromFile(absPath, srcDir)
76
+ return { id, path: defaultPageRoute(id) }
77
+ }
78
+
69
79
  /**
70
80
  * Walks `srcDir` and returns the user-authored entries the platform cares
71
81
  * about, bucketed by kind. Empty (zero-byte) source files are skipped — we
@@ -79,21 +89,20 @@ export function defaultPageRoute (id) {
79
89
  * @returns {Promise<PlatformFiles>}
80
90
  */
81
91
  export default async function getPlatformFiles (srcDir) {
82
- const out = { pages: [], apis: [], tasks: [] }
92
+ const out = { pages: [], apis: [], tasks: [], resources: [] }
83
93
  const all = [
84
94
  ...discoverFilesByPattern(srcDir, PAGE_FILE_PATTERN),
85
95
  ...discoverFilesByPattern(srcDir, API_FILE_PATTERN),
86
96
  ...discoverFilesByPattern(srcDir, TASK_FILE_PATTERN),
97
+ ...discoverFilesByPattern(srcDir, RESOURCE_FILE_PATTERN),
87
98
  ]
99
+ const bucketByKind = { page: 'pages', api: 'apis', task: 'tasks', resource: 'resources' }
88
100
  for (const sourcePath of all) {
89
101
  const stat = fs.statSync(sourcePath)
90
102
  if (!stat.isFile() || stat.size === 0) continue
91
103
  const kind = classifyFile(sourcePath)
92
104
  if (!kind) continue
93
- out[kind === 'page' ? 'pages' : kind === 'api' ? 'apis' : 'tasks'].push({
94
- kind,
95
- sourcePath,
96
- })
105
+ out[bucketByKind[kind]].push({ kind, sourcePath })
97
106
  }
98
107
  return out
99
108
  }
@@ -39,8 +39,9 @@ import { metadataIdFromFile, defaultPageRoute } from './get-platform-files.task.
39
39
  * (e.g. `/static/home.page-7f2a.js`).
40
40
  *
41
41
  * @typedef {object} Manifest
42
- * @property {ManifestEntry[]} entries Flat, ordered list of every routable thing.
43
- * @property {object} config Inlined `src/config.js` default export.
42
+ * @property {ManifestEntry[]} entries Flat, ordered list of every routable thing.
43
+ * @property {object[]} resourceTemplates Each `*.resource.js` file's default-exported template object.
44
+ * @property {object} config Inlined `src/config.js` default export.
44
45
  */
45
46
 
46
47
  /**
@@ -83,7 +84,10 @@ export function manifestPlugin ({ entriesByStub, srcDir, staticOutDir, configVal
83
84
  async writeBundle (_, bundle) {
84
85
  /** @type {ManifestEntry[]} */
85
86
  const entries = []
87
+ /** @type {object[]} */
88
+ const resourceTemplates = []
86
89
  const seenIds = { page: new Set(), api: new Set(), task: new Set() }
90
+ const seenResourceIds = new Set()
87
91
 
88
92
  for (const fileName of Object.keys(bundle)) {
89
93
  const chunk = bundle[fileName]
@@ -109,6 +113,30 @@ export function manifestPlugin ({ entriesByStub, srcDir, staticOutDir, configVal
109
113
  )
110
114
  }
111
115
 
116
+ // Resources are pure data — the default export *is* the template.
117
+ // They don't use `metadata`, don't need a derived id, and don't
118
+ // produce a routable manifest entry; they accumulate into a separate
119
+ // `resourceTemplates` array on the manifest.
120
+ if (entryInfo.kind === 'resource') {
121
+ const template = mod && mod.default
122
+ if (!template || typeof template !== 'object' || Array.isArray(template)) {
123
+ this.error(
124
+ `[@ossy/app][build] resource entry ${entryInfo.sourcePath} must default-export an object`,
125
+ )
126
+ }
127
+ if (typeof template.id !== 'string' || template.id.trim() === '') {
128
+ this.error(
129
+ `[@ossy/app][build] resource entry ${entryInfo.sourcePath} default export is missing a non-empty string "id"`,
130
+ )
131
+ }
132
+ if (seenResourceIds.has(template.id)) {
133
+ this.error(`[@ossy/app][build] Duplicate resource template id "${template.id}"`)
134
+ }
135
+ seenResourceIds.add(template.id)
136
+ resourceTemplates.push(template)
137
+ continue
138
+ }
139
+
112
140
  const rawMeta = (mod && mod.metadata) || {}
113
141
  const id = rawMeta.id || metadataIdFromFile(entryInfo.sourcePath, srcDir)
114
142
  if (!id) {
@@ -141,7 +169,7 @@ export function manifestPlugin ({ entriesByStub, srcDir, staticOutDir, configVal
141
169
  }
142
170
 
143
171
  /** @type {Manifest} */
144
- const manifest = { entries, config: configValue || {} }
172
+ const manifest = { entries, resourceTemplates, config: configValue || {} }
145
173
  fs.mkdirSync(path.dirname(manifestPath), { recursive: true })
146
174
  fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), 'utf8')
147
175
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ossy/app",
3
- "version": "1.16.5",
3
+ "version": "1.16.7",
4
4
  "description": "",
5
5
  "source": "./src/index.js",
6
6
  "main": "./src/index.js",
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "scripts": {
18
18
  "build": "echo Building not required",
19
- "test": "node --experimental-vm-modules ../node_modules/jest/bin/jest.js --verbose",
19
+ "test": "NODE_OPTIONS=--experimental-vm-modules jest --verbose",
20
20
  "typecheck": "tsc --noEmit"
21
21
  },
22
22
  "keywords": [],
@@ -33,15 +33,15 @@
33
33
  "@babel/eslint-parser": "^7.15.8",
34
34
  "@babel/preset-react": "^7.26.3",
35
35
  "@babel/register": "^7.25.9",
36
- "@ossy/connected-components": "^1.16.5",
37
- "@ossy/design-system": "^1.16.5",
38
- "@ossy/pages": "^1.16.5",
39
- "@ossy/platform": "^1.15.5",
40
- "@ossy/router": "^1.16.5",
41
- "@ossy/router-react": "^1.16.5",
42
- "@ossy/sdk": "^1.16.5",
43
- "@ossy/sdk-react": "^1.16.5",
44
- "@ossy/themes": "^1.16.5",
36
+ "@ossy/connected-components": "^1.16.7",
37
+ "@ossy/design-system": "^1.16.7",
38
+ "@ossy/pages": "^1.16.7",
39
+ "@ossy/platform": "^1.15.7",
40
+ "@ossy/router": "^1.16.7",
41
+ "@ossy/router-react": "^1.16.7",
42
+ "@ossy/sdk": "^1.16.7",
43
+ "@ossy/sdk-react": "^1.16.7",
44
+ "@ossy/themes": "^1.16.7",
45
45
  "@rollup/plugin-alias": "^6.0.0",
46
46
  "@rollup/plugin-babel": "6.1.0",
47
47
  "@rollup/plugin-commonjs": "^29.0.0",
@@ -75,5 +75,5 @@
75
75
  "README.md",
76
76
  "tsconfig.json"
77
77
  ],
78
- "gitHead": "da7427b2b46b9069ff9a1a05751e275eb11ddc50"
78
+ "gitHead": "ca79da159a5d017ea80dfddbb7218b1a51aab396"
79
79
  }
package/src/index.js CHANGED
@@ -3,4 +3,5 @@ export {
3
3
  PAGE_FILE_PATTERN,
4
4
  API_FILE_PATTERN,
5
5
  TASK_FILE_PATTERN,
6
+ RESOURCE_FILE_PATTERN,
6
7
  } from '../cli/build.task.js'