@ossy/app 0.15.12 → 0.15.13

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/README.md CHANGED
@@ -25,6 +25,12 @@ For a single-file setup, use `src/pages.jsx` (legacy).
25
25
 
26
26
  Split pages are merged into **`build/.ossy/pages.generated.jsx`** (next to other build output) during `dev` / `build`.
27
27
 
28
+ List **`modules: ['@your/npm-package', ...]`** in `src/config.js` to pull in every **`*.page.js`**, **`*.page.jsx`**, **`*.page.ts`**, and **`*.page.tsx`** file under each package’s **`src/`** tree (any subfolder). Deprecated: **`pagesModules`** / **`pagesModule`** — still read for now, with a deprecation warning.
29
+
30
+ Optional **`src/page-shell.jsx`** (or `.js`): default export your site layout component. **`usePageShell()`** from `@ossy/connected-components` wraps module pages in that shell so packages do not import site paths.
31
+
32
+ **Note:** **`@ossy/connected-components`** is slated for retirement; its responsibilities (app shell, routing wrapper, **`usePageShell`**, etc.) should move into **`@ossy/app`** or smaller focused packages over time. See that package’s README.
33
+
28
34
  Add `src/config.js` for workspace and theme:
29
35
 
30
36
  ```js
package/cli/build.js CHANGED
@@ -37,6 +37,17 @@ export function ensureOssyGeneratedDir (buildPath) {
37
37
  return dir
38
38
  }
39
39
 
40
+ export function resolvePageShellSource (cwd, scriptDir) {
41
+ const candidates = [
42
+ path.resolve(cwd, 'src/page-shell.jsx'),
43
+ path.resolve(cwd, 'src/page-shell.js'),
44
+ ]
45
+ for (const p of candidates) {
46
+ if (fs.existsSync(p)) return p
47
+ }
48
+ return path.resolve(scriptDir, 'page-shell-default.jsx')
49
+ }
50
+
40
51
  function relToGeneratedImport (generatedAbs, targetAbs) {
41
52
  return path.relative(path.dirname(generatedAbs), targetAbs).replace(/\\/g, '/')
42
53
  }
@@ -280,6 +291,27 @@ export function filePathToRoute(filePath, srcDir) {
280
291
  return { id, path: routePath }
281
292
  }
282
293
 
294
+ /** Route base for `filePathToRoute`: site `--pages` dir, or a package’s `src` dir (first `src` segment in the path). */
295
+ export function pageFilesRouteBaseDir (filePath, sitePagesDir) {
296
+ const fp = path.resolve(filePath)
297
+ const siteRoot = path.resolve(sitePagesDir)
298
+ const sitePrefix = siteRoot + path.sep
299
+ if (fp === siteRoot || fp.startsWith(sitePrefix)) {
300
+ return siteRoot
301
+ }
302
+ const parts = fp.split(path.sep)
303
+ const srcIdx = parts.indexOf('src')
304
+ if (srcIdx !== -1) {
305
+ return parts.slice(0, srcIdx + 1).join(path.sep)
306
+ }
307
+ return siteRoot
308
+ }
309
+
310
+ /** Derive `{ id, path }` for build overview when mixing site pages and installable `modules` package paths. */
311
+ export function routeForPageFileOverview (filePath, sitePagesDir) {
312
+ return filePathToRoute(filePath, pageFilesRouteBaseDir(filePath, sitePagesDir))
313
+ }
314
+
283
315
  export function generatePagesModule (pageFiles, cwd, srcDir, generatedPath) {
284
316
  const resolvedSrc = path.resolve(cwd, srcDir)
285
317
  const lines = [
@@ -300,7 +332,8 @@ export function generatePagesModule (pageFiles, cwd, srcDir, generatedPath) {
300
332
  '',
301
333
  'export default [',
302
334
  ...pageFiles.map((f, i) => {
303
- const { id, path: defaultPath } = filePathToRoute(f, resolvedSrc)
335
+ const base = pageFilesRouteBaseDir(f, resolvedSrc)
336
+ const { id, path: defaultPath } = filePathToRoute(f, base)
304
337
  const pathStr = JSON.stringify(defaultPath)
305
338
  return ` toPage(_page${i}, { id: '${id}', path: ${pathStr} }),`
306
339
  }),
@@ -309,6 +342,21 @@ export function generatePagesModule (pageFiles, cwd, srcDir, generatedPath) {
309
342
  return lines.join('\n')
310
343
  }
311
344
 
345
+ function installableModulePackagesFromCfgObject (cfg, warn) {
346
+ if (Array.isArray(cfg.modules) && cfg.modules.length) {
347
+ return cfg.modules
348
+ }
349
+ if (Array.isArray(cfg.pagesModules) && cfg.pagesModules.length) {
350
+ warn?.('[@ossy/app][build] `pagesModules` is deprecated; use `modules` in src/config.js')
351
+ return cfg.pagesModules
352
+ }
353
+ if (typeof cfg.pagesModule === 'string') {
354
+ warn?.('[@ossy/app][build] `pagesModule` is deprecated; use `modules: [ ... ]` in src/config.js')
355
+ return [cfg.pagesModule]
356
+ }
357
+ return []
358
+ }
359
+
312
360
  export async function discoverModulePageFiles({ cwd, configPath }) {
313
361
  if (!configPath || !fs.existsSync(configPath)) return []
314
362
  try {
@@ -316,54 +364,59 @@ export async function discoverModulePageFiles({ cwd, configPath }) {
316
364
  // importable (configs often import theme/template modules that may not be
317
365
  // resolvable in the build-time node context).
318
366
  const cfgSource = fs.readFileSync(configPath, 'utf8')
319
- const modules = []
367
+ const moduleNames = []
368
+ let warnedLegacy = false
369
+ const warnOnce = (msg) => {
370
+ if (!warnedLegacy) {
371
+ console.warn(msg)
372
+ warnedLegacy = true
373
+ }
374
+ }
320
375
 
321
- // pagesModules: ['a', "b"]
322
- const arrMatch = cfgSource.match(/pagesModules\s*:\s*\[([^\]]*)\]/m)
323
- if (arrMatch?.[1]) {
324
- const body = arrMatch[1]
376
+ const modulesArr = cfgSource.match(/\bmodules\s*:\s*\[([^\]]*)\]/m)
377
+ const pagesModulesArr = cfgSource.match(/\bpagesModules\s*:\s*\[([^\]]*)\]/m)
378
+ const listBody = modulesArr?.[1] ?? pagesModulesArr?.[1]
379
+ if (listBody !== undefined && listBody !== null) {
380
+ if (pagesModulesArr && !modulesArr) {
381
+ warnOnce('[@ossy/app][build] `pagesModules` is deprecated; use `modules` in src/config.js')
382
+ }
325
383
  const re = /['"]([^'"]+)['"]/g
326
384
  let m
327
- while ((m = re.exec(body)) !== null) modules.push(m[1])
385
+ while ((m = re.exec(listBody)) !== null) moduleNames.push(m[1])
328
386
  }
329
387
 
330
- // pagesModule: 'a'
331
- if (modules.length === 0) {
332
- const singleMatch = cfgSource.match(/pagesModule\s*:\s*['"]([^'"]+)['"]/m)
333
- if (singleMatch?.[1]) modules.push(singleMatch[1])
388
+ if (moduleNames.length === 0) {
389
+ const singlePagesModule = cfgSource.match(/\bpagesModule\s*:\s*['"]([^'"]+)['"]/m)
390
+ if (singlePagesModule?.[1]) {
391
+ warnOnce('[@ossy/app][build] `pagesModule` is deprecated; use `modules: [ ... ]` in src/config.js')
392
+ moduleNames.push(singlePagesModule[1])
393
+ }
334
394
  }
335
395
 
336
- if (modules.length) {
396
+ const loadFromNames = (names) => {
337
397
  const req = createRequire(path.resolve(cwd, 'package.json'))
338
398
  const files = []
339
- for (const name of modules) {
399
+ for (const name of names) {
340
400
  const pkgJsonPath = req.resolve(`${name}/package.json`)
341
401
  const pkgDir = path.dirname(pkgJsonPath)
342
- const modulePagesDir = path.join(pkgDir, 'src', 'pages')
343
- files.push(...discoverPageFiles(modulePagesDir))
402
+ const moduleSrcDir = path.join(pkgDir, 'src')
403
+ files.push(...discoverPageFiles(moduleSrcDir))
344
404
  }
345
405
  return files
346
406
  }
347
407
 
408
+ if (moduleNames.length) {
409
+ return loadFromNames(moduleNames)
410
+ }
411
+
348
412
  const mod = await import(url.pathToFileURL(configPath))
349
413
  const cfg = mod?.default ?? mod ?? {}
350
- const modules2 = Array.isArray(cfg.pagesModules)
351
- ? cfg.pagesModules
352
- : (typeof cfg.pagesModule === 'string' ? [cfg.pagesModule] : [])
414
+ const modules2 = installableModulePackagesFromCfgObject(cfg, (msg) => console.warn(msg))
353
415
 
354
416
  if (!modules2.length) return []
355
-
356
- const req = createRequire(path.resolve(cwd, 'package.json'))
357
- const files = []
358
- for (const name of modules2) {
359
- const pkgJsonPath = req.resolve(`${name}/package.json`)
360
- const pkgDir = path.dirname(pkgJsonPath)
361
- const modulePagesDir = path.join(pkgDir, 'src', 'pages')
362
- files.push(...discoverPageFiles(modulePagesDir))
363
- }
364
- return files
417
+ return loadFromNames(modules2)
365
418
  } catch (e) {
366
- console.warn('[@ossy/app][build] pagesModules config could not be loaded; continuing without modules')
419
+ console.warn('[@ossy/app][build] `modules` config could not be loaded; continuing without installable module pages')
367
420
  return []
368
421
  }
369
422
  }
@@ -421,9 +474,10 @@ export function printBuildOverview({
421
474
  console.log(` \x1b[36mAPI:\x1b[0m ${rel(apiSourcePath)}`)
422
475
  console.log(' ' + '─'.repeat(50))
423
476
 
424
- const pages = isPageFiles && pageFiles?.length
425
- ? pageFiles.map((f) => filePathToRoute(f, srcDir))
426
- : parsePagesFromSource(pagesSourcePath)
477
+ const pages =
478
+ isPageFiles && pageFiles?.length
479
+ ? pageFiles.map((f) => routeForPageFileOverview(f, srcDir))
480
+ : parsePagesFromSource(pagesSourcePath)
427
481
  if (pages.length > 0) {
428
482
  console.log(' \x1b[36mRoutes:\x1b[0m')
429
483
  const maxId = Math.max(6, ...pages.map((p) => String(p.id).length))
@@ -635,7 +689,7 @@ export const build = async (cliArgs) => {
635
689
  apiOverviewFiles,
636
690
  configPath,
637
691
  isPageFiles,
638
- pageFiles: isPageFiles ? pageFiles : [],
692
+ pageFiles: isPageFiles ? [...pageFiles, ...modulePageFiles] : [],
639
693
  });
640
694
 
641
695
  if (!fs.existsSync(apiSourcePath)) {
@@ -650,6 +704,8 @@ export const build = async (cliArgs) => {
650
704
  ? configPath
651
705
  : path.resolve(scriptDir, 'default-config.js')
652
706
 
707
+ const pageShellSourcePath = resolvePageShellSource(cwd, scriptDir)
708
+
653
709
  const inputOptions = {
654
710
  input: inputFiles,
655
711
  plugins: [
@@ -680,6 +736,11 @@ export const build = async (cliArgs) => {
680
736
  delimiters: ['%%', '%%'],
681
737
  '@ossy/config/source-file': configSourcePath,
682
738
  }),
739
+ replace({
740
+ preventAssignment: true,
741
+ delimiters: ['%%', '%%'],
742
+ '@ossy/page-shell/source-file': pageShellSourcePath,
743
+ }),
683
744
  replace({
684
745
  preventAssignment: true,
685
746
  'process.env.NODE_ENV': JSON.stringify('production')
@@ -1,10 +1,11 @@
1
1
  import React from 'react'
2
2
  import { App } from '@ossy/connected-components'
3
3
  import pages from '%%@ossy/pages/source-file%%'
4
+ import pageShell from '%%@ossy/page-shell/source-file%%'
4
5
 
5
6
  /**
6
7
  * App entry. Uses App from @ossy/connected-components with pages from src/pages.jsx.
7
8
  */
8
- export default function DefaultApp(config) {
9
- return React.createElement(App, { ...config, pages })
9
+ export default function DefaultApp (config) {
10
+ return React.createElement(App, { ...config, pages, pageShell })
10
11
  }
package/cli/dev.js CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  ossyGeneratedDir,
13
13
  OSSY_GEN_PAGES_BASENAME,
14
14
  OSSY_GEN_API_BASENAME,
15
+ resolvePageShellSource,
15
16
  } from './build.js';
16
17
  import { watch } from 'rollup';
17
18
  import babel from '@rollup/plugin-babel';
@@ -94,7 +95,7 @@ export const dev = async (cliArgs) => {
94
95
  apiOverviewFiles,
95
96
  configPath,
96
97
  isPageFiles,
97
- pageFiles: isPageFiles ? pageFiles : [],
98
+ pageFiles: isPageFiles ? [...pageFiles, ...modulePageFiles] : [],
98
99
  });
99
100
 
100
101
  if (!fs.existsSync(apiSourcePath)) {
@@ -109,6 +110,8 @@ export const dev = async (cliArgs) => {
109
110
  ? configPath
110
111
  : path.resolve(scriptDir, 'default-config.js')
111
112
 
113
+ const pageShellSourcePath = resolvePageShellSource(cwd, scriptDir)
114
+
112
115
  const inputOptions = {
113
116
  input: inputFiles,
114
117
  plugins: [
@@ -139,6 +142,11 @@ export const dev = async (cliArgs) => {
139
142
  delimiters: ['%%', '%%'],
140
143
  '@ossy/config/source-file': configSourcePath,
141
144
  }),
145
+ replace({
146
+ preventAssignment: true,
147
+ delimiters: ['%%', '%%'],
148
+ '@ossy/page-shell/source-file': pageShellSourcePath,
149
+ }),
142
150
  replace({
143
151
  preventAssignment: true,
144
152
  'process.env.NODE_ENV': JSON.stringify('development')
@@ -0,0 +1,9 @@
1
+ import React from 'react'
2
+
3
+ /**
4
+ * Used when the site has no `src/page-shell.jsx`.
5
+ * Module pages still call {@link usePageShell} but get a passthrough.
6
+ */
7
+ export default function PageShellDefault ({ children }) {
8
+ return children
9
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ossy/app",
3
- "version": "0.15.12",
3
+ "version": "0.15.13",
4
4
  "description": "",
5
5
  "source": "./src/index.js",
6
6
  "main": "./src/index.js",
@@ -66,5 +66,5 @@
66
66
  "README.md",
67
67
  "tsconfig.json"
68
68
  ],
69
- "gitHead": "1da35d94f324f917d5ea790f838ce8e50214730e"
69
+ "gitHead": "280ce1f62d7daf721909ac5eabe4181991e00456"
70
70
  }