@ossy/app 0.9.0 → 0.10.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/README.md CHANGED
@@ -1,6 +1,68 @@
1
1
  # `@ossy/app`
2
2
 
3
- Server-side rendering runtime for Ossy apps.
3
+ Server-side rendering runtime and build tooling for Ossy apps. Use with `@ossy/cli` for the convention-based setup (`npx @ossy/cli dev`).
4
+
5
+ For custom setups (Next.js, Vite, etc.), use `@ossy/connected-components` directly — see the [root README](../README.md#when-to-use-what).
6
+
7
+ ## Setup
8
+
9
+ Create `*.page.jsx` files in `src/`:
10
+
11
+ ```jsx
12
+ // src/home.page.jsx
13
+ import React from 'react'
14
+ export default () => <h1>Welcome</h1>
15
+ ```
16
+
17
+ Each file becomes a route: `home.page.jsx` → `/`, `about.page.jsx` → `/about`. Optionally export `metadata` for custom id/path or multi-language:
18
+
19
+ ```js
20
+ export const metadata = { path: { en: '/about', sv: '/om' } }
21
+ export default () => <h1>About</h1>
22
+ ```
23
+
24
+ For a single-file setup, use `src/pages.jsx` (legacy).
25
+
26
+ Add `src/config.js` for workspace and theme:
27
+
28
+ ```js
29
+ import { CloudLight } from '@ossy/themes'
30
+
31
+ export default {
32
+ workspaceId: 'your-workspace-id',
33
+ theme: CloudLight, // or 'light' | 'dark' | CloudDark
34
+ apiUrl: 'https://api.ossy.se/api/v0', // optional
35
+ }
36
+ ```
37
+
38
+ Config is loaded at build time and merged with request-time settings (e.g. user theme preference from cookies). The server passes `workspaceId`, `apiUrl`, and `theme` to the App component.
39
+
40
+ Run `npx @ossy/cli dev` or `npx @ossy/cli build`.
41
+
42
+ ## API routes
43
+
44
+ Create `src/api.js` to define custom API endpoints. Each route must have `id`, `path`, and a `handle(req, res)` function:
45
+
46
+ ```js
47
+ export default [
48
+ {
49
+ id: 'health',
50
+ path: '/api/health',
51
+ handle(req, res) {
52
+ res.json({ status: 'ok' })
53
+ },
54
+ },
55
+ {
56
+ id: 'users',
57
+ path: '/api/users',
58
+ handle(req, res) {
59
+ res.json({ users: [] })
60
+ },
61
+ },
62
+ ]
63
+ ```
64
+
65
+ API routes are matched before the app is rendered. The router supports dynamic segments (e.g. `path: '/api/users/:id'`); extract params from `req.originalUrl` if needed. Use paths that don't conflict with `/@ossy/*` (reserved for the internal proxy).
4
66
 
5
67
  ## Port configuration
6
68
 
package/cli/build.js CHANGED
@@ -16,12 +16,143 @@ import remove from 'rollup-plugin-delete';
16
16
  import arg from 'arg'
17
17
  // import inject from '@rollup/plugin-inject'
18
18
 
19
+ const PAGE_FILE_PATTERN = /\.page\.(jsx?|tsx?)$/
20
+
21
+ export function discoverPageFiles(srcDir) {
22
+ const dir = path.resolve(srcDir)
23
+ if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
24
+ return []
25
+ }
26
+ const files = []
27
+ const walk = (d) => {
28
+ const entries = fs.readdirSync(d, { withFileTypes: true })
29
+ for (const e of entries) {
30
+ const full = path.join(d, e.name)
31
+ if (e.isDirectory()) walk(full)
32
+ else if (PAGE_FILE_PATTERN.test(e.name)) files.push(full)
33
+ }
34
+ }
35
+ walk(dir)
36
+ return files.sort()
37
+ }
38
+
39
+ export function filePathToRoute(filePath, srcDir) {
40
+ const rel = path.relative(srcDir, filePath).replace(/\\/g, '/')
41
+ let pathPart = rel.replace(PAGE_FILE_PATTERN, '').replace(/\/index$/, '').replace(/\/home$/, '') || 'home'
42
+ if (pathPart === 'index' || pathPart === 'home') pathPart = 'home'
43
+ const id = pathPart === 'home' ? 'home' : pathPart.replace(/\//g, '-')
44
+ const routePath = pathPart === 'home' ? '/' : '/' + pathPart
45
+ return { id, path: routePath }
46
+ }
47
+
48
+ export function generatePagesModule(pageFiles, cwd, srcDir = 'src') {
49
+ const resolvedSrc = path.resolve(cwd, srcDir)
50
+ const lines = [
51
+ "import React from 'react'",
52
+ ...pageFiles.map((f, i) => {
53
+ const rel = path.relative(cwd, f).replace(/\\/g, '/')
54
+ return `import * as _page${i} from './${rel}'`
55
+ }),
56
+ '',
57
+ 'function toPage(mod, derived) {',
58
+ ' const meta = mod?.metadata || {}',
59
+ " const def = mod?.default",
60
+ " if (typeof def === 'function') {",
61
+ " return { ...derived, ...meta, element: React.createElement(def) }",
62
+ ' }',
63
+ " return { ...derived, ...meta, ...(def || {}) }",
64
+ '}',
65
+ '',
66
+ 'export default [',
67
+ ...pageFiles.map((f, i) => {
68
+ const { id, path: defaultPath } = filePathToRoute(f, resolvedSrc)
69
+ const pathStr = typeof defaultPath === 'string' ? defaultPath : JSON.stringify(defaultPath)
70
+ return ` toPage(_page${i}, { id: '${id}', path: ${pathStr} }),`
71
+ }),
72
+ ']',
73
+ ]
74
+ return lines.join('\n')
75
+ }
76
+
77
+ export function parsePagesFromSource(filePath) {
78
+ try {
79
+ const content = fs.readFileSync(filePath, 'utf8')
80
+ const items = []
81
+ // Match { id: 'x', path: '/y' } or { id: "x", path: "/y" }
82
+ const idPathPattern = /\{\s*id\s*:\s*['"]([^'"]*)['"]\s*,\s*path\s*:\s*['"]([^'"]*)['"]/g
83
+ // Match { path: '/y', element: ... } (path-first)
84
+ const pathElementPattern = /\{\s*path\s*:\s*['"]([^'"]*)['"]\s*,\s*element\s*:/g
85
+ // Match { path: { en: '/x', sv: '/y' }, ... }
86
+ const pathObjPattern = /\{\s*path\s*:\s*\{\s*([^}]+)\}/g
87
+ let m
88
+ while ((m = idPathPattern.exec(content)) !== null) {
89
+ items.push({ id: m[1], path: m[2] })
90
+ }
91
+ if (items.length === 0) {
92
+ while ((m = pathElementPattern.exec(content)) !== null) {
93
+ const p = m[1]
94
+ const id = p === '/' ? 'home' : p.replace(/^\//, '').replace(/\/$/, '').replace(/\//g, '-') || 'page'
95
+ items.push({ id, path: p })
96
+ }
97
+ }
98
+ if (items.length === 0) {
99
+ while ((m = pathObjPattern.exec(content)) !== null) {
100
+ const pathStr = m[1].replace(/\s/g, '').replace(/:/g, ': ')
101
+ items.push({ id: 'page', path: pathStr })
102
+ }
103
+ }
104
+ return items
105
+ } catch {
106
+ return []
107
+ }
108
+ }
109
+
110
+ export function printBuildOverview({ pagesSourcePath, apiSourcePath, configPath, isPageFiles, pageFiles }) {
111
+ const rel = (p) => path.relative(process.cwd(), p)
112
+ const cwd = process.cwd()
113
+ const srcDir = path.resolve(cwd, 'src')
114
+ console.log('\n \x1b[1mBuild overview\x1b[0m')
115
+ console.log(' ' + '─'.repeat(50))
116
+ console.log(` \x1b[36mPages:\x1b[0m ${rel(pagesSourcePath)}`)
117
+ if (fs.existsSync(configPath)) {
118
+ console.log(` \x1b[36mConfig:\x1b[0m ${rel(configPath)}`)
119
+ }
120
+ console.log(' ' + '─'.repeat(50))
121
+
122
+ const pages = isPageFiles && pageFiles?.length
123
+ ? pageFiles.map((f) => filePathToRoute(f, srcDir))
124
+ : parsePagesFromSource(pagesSourcePath)
125
+ if (pages.length > 0) {
126
+ console.log(' \x1b[36mRoutes:\x1b[0m')
127
+ const maxId = Math.max(6, ...pages.map((p) => String(p.id).length))
128
+ const maxPath = Math.max(6, ...pages.map((p) => String(p.path).length))
129
+ pages.forEach((p) => {
130
+ const id = String(p.id).padEnd(maxId)
131
+ const pathStr = String(p.path).padEnd(maxPath)
132
+ console.log(` ${id} ${pathStr}`)
133
+ })
134
+ } else {
135
+ console.log(' \x1b[33mRoutes:\x1b[0m (could not parse or empty)')
136
+ }
137
+
138
+ if (fs.existsSync(apiSourcePath)) {
139
+ const apiRoutes = parsePagesFromSource(apiSourcePath)
140
+ if (apiRoutes.length > 0) {
141
+ console.log(' \x1b[36mAPI routes:\x1b[0m')
142
+ apiRoutes.forEach((r) => {
143
+ console.log(` ${r.id} ${r.path}`)
144
+ })
145
+ }
146
+ }
147
+ console.log(' ' + '─'.repeat(50) + '\n')
148
+ }
149
+
19
150
  export const build = async (cliArgs) => {
20
151
  console.log('[@ossy/app][build] Starting...')
21
152
 
22
153
  const options = arg({
23
- '--source': String,
24
- '--s': '--source',
154
+ '--pages': String,
155
+ '--p': '--pages',
25
156
 
26
157
  '--destination': String,
27
158
  '--d': '--destination',
@@ -31,22 +162,40 @@ export const build = async (cliArgs) => {
31
162
  }, { argv: cliArgs })
32
163
 
33
164
 
34
- const appSourcePath = path.resolve(options['--source'] || 'src/App.jsx');
165
+ const scriptDir = path.dirname(url.fileURLToPath(import.meta.url))
166
+ const cwd = process.cwd()
167
+ const pagesOpt = options['--pages'] || 'src'
168
+ const srcDir = path.resolve(pagesOpt)
169
+ const pageFiles = discoverPageFiles(srcDir)
170
+ const pagesJsxPath = path.resolve('src/pages.jsx')
171
+ const hasPagesJsx = fs.existsSync(pagesJsxPath)
172
+
173
+ let effectivePagesSource
174
+ let isPageFiles = false
175
+ if (pageFiles.length > 0) {
176
+ const generatedPath = path.join(cwd, '.ossy-pages.generated.jsx')
177
+ fs.writeFileSync(generatedPath, generatePagesModule(pageFiles, cwd, pagesOpt))
178
+ effectivePagesSource = generatedPath
179
+ isPageFiles = true
180
+ } else if (hasPagesJsx) {
181
+ effectivePagesSource = pagesJsxPath
182
+ } else {
183
+ throw new Error(`[@ossy/app][build] No pages found. Create *.page.jsx files in src/, or src/pages.jsx`);
184
+ }
185
+
35
186
  let apiSourcePath = path.resolve(options['--api-source'] || 'src/api.js');
36
187
  let middlewareSourcePath = path.resolve(options['--middleware-source'] || 'src/middleware.js');
37
188
  const configPath = path.resolve(options['--config'] || 'src/config.js');
38
189
  const buildPath = path.resolve(options['--destination'] || 'build');
39
190
  const publicDir = path.resolve('public')
40
191
 
41
- const scriptDir = path.dirname(url.fileURLToPath(import.meta.url))
42
192
  const inputClient = path.resolve(scriptDir, 'client.js')
43
193
  const inputServer = path.resolve(scriptDir, 'server.js')
44
194
 
45
195
  const inputFiles = [inputClient, inputServer]
46
196
 
47
- if (!fs.existsSync(appSourcePath)) {
48
- throw new Error(`[@ossy/app][build] Source path does not exist: ${appSourcePath}`);
49
- }
197
+ const appEntryPath = path.resolve(scriptDir, 'default-app.jsx')
198
+ printBuildOverview({ pagesSourcePath: effectivePagesSource, apiSourcePath, configPath, isPageFiles, pageFiles: isPageFiles ? pageFiles : [] });
50
199
 
51
200
  if (!fs.existsSync(apiSourcePath)) {
52
201
  apiSourcePath = path.resolve(scriptDir, 'api.js')
@@ -56,9 +205,9 @@ export const build = async (cliArgs) => {
56
205
  middlewareSourcePath = path.resolve(scriptDir, 'middleware.js')
57
206
  }
58
207
 
59
- if (fs.existsSync(configPath)) {
60
- inputFiles.push(configPath)
61
- }
208
+ const configSourcePath = fs.existsSync(configPath)
209
+ ? configPath
210
+ : path.resolve(scriptDir, 'default-config.js')
62
211
 
63
212
  const inputOptions = {
64
213
  input: inputFiles,
@@ -68,7 +217,12 @@ export const build = async (cliArgs) => {
68
217
  replace({
69
218
  preventAssignment: true,
70
219
  delimiters: ['%%', '%%'],
71
- '@ossy/app/source-file': appSourcePath,
220
+ '@ossy/app/source-file': appEntryPath,
221
+ }),
222
+ replace({
223
+ preventAssignment: true,
224
+ delimiters: ['%%', '%%'],
225
+ '@ossy/pages/source-file': effectivePagesSource,
72
226
  }),
73
227
  replace({
74
228
  preventAssignment: true,
@@ -80,6 +234,11 @@ export const build = async (cliArgs) => {
80
234
  delimiters: ['%%', '%%'],
81
235
  '@ossy/middleware/source-file': middlewareSourcePath,
82
236
  }),
237
+ replace({
238
+ preventAssignment: true,
239
+ delimiters: ['%%', '%%'],
240
+ '@ossy/config/source-file': configSourcePath,
241
+ }),
83
242
  replace({
84
243
  preventAssignment: true,
85
244
  'process.env.NODE_ENV': JSON.stringify('production')
@@ -0,0 +1,10 @@
1
+ import React from 'react'
2
+ import { App } from '@ossy/connected-components'
3
+ import pages from '%%@ossy/pages/source-file%%'
4
+
5
+ /**
6
+ * App entry. Uses App from @ossy/connected-components with pages from src/pages.jsx.
7
+ */
8
+ export default function DefaultApp(config) {
9
+ return React.createElement(App, { ...config, pages })
10
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Default config when src/config.js does not exist.
3
+ * Provides empty defaults so the server can always merge config.
4
+ */
5
+ export default {}
package/cli/dev.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import path from 'path';
2
2
  import url from 'url';
3
3
  import fs from 'fs';
4
+ import { printBuildOverview, discoverPageFiles, generatePagesModule } from './build.js';
4
5
  import { watch } from 'rollup';
5
6
  import babel from '@rollup/plugin-babel';
6
7
  import { nodeResolve as resolveDependencies } from '@rollup/plugin-node-resolve'
@@ -21,8 +22,8 @@ export const dev = async (cliArgs) => {
21
22
  console.log('[@ossy/app][dev] Starting...')
22
23
 
23
24
  const options = arg({
24
- '--source': String,
25
- '--s': '--source',
25
+ '--pages': String,
26
+ '--p': '--pages',
26
27
 
27
28
  '--destination': String,
28
29
  '--d': '--destination',
@@ -32,22 +33,39 @@ export const dev = async (cliArgs) => {
32
33
  }, { argv: cliArgs, permissive: true })
33
34
 
34
35
 
35
- const appSourcePath = path.resolve(options['--source'] || 'src/App.jsx');
36
+ const scriptDir = path.dirname(url.fileURLToPath(import.meta.url))
37
+ const cwd = process.cwd()
38
+ const pagesOpt = options['--pages'] || 'src'
39
+ const srcDir = path.resolve(pagesOpt)
40
+ const pageFiles = discoverPageFiles(srcDir)
41
+ const pagesJsxPath = path.resolve('src/pages.jsx')
42
+ const hasPagesJsx = fs.existsSync(pagesJsxPath)
43
+
44
+ let effectivePagesSource
45
+ let isPageFiles = false
46
+ if (pageFiles.length > 0) {
47
+ const generatedPath = path.join(cwd, '.ossy-pages.generated.jsx')
48
+ fs.writeFileSync(generatedPath, generatePagesModule(pageFiles, cwd, pagesOpt))
49
+ effectivePagesSource = generatedPath
50
+ isPageFiles = true
51
+ } else if (hasPagesJsx) {
52
+ effectivePagesSource = pagesJsxPath
53
+ } else {
54
+ throw new Error(`[@ossy/app][dev] No pages found. Create *.page.jsx files in src/, or src/pages.jsx`);
55
+ }
56
+
36
57
  let apiSourcePath = path.resolve(options['--api-source'] || 'src/api.js');
37
58
  let middlewareSourcePath = path.resolve(options['--middleware-source'] || 'src/middleware.js');
38
59
  const configPath = path.resolve(options['--config'] || 'src/config.js');
39
60
  const buildPath = path.resolve(options['--destination'] || 'build');
40
61
  const publicDir = path.resolve('public')
41
62
 
42
- const scriptDir = path.dirname(url.fileURLToPath(import.meta.url))
43
63
  const inputClient = path.resolve(scriptDir, 'client.js')
44
64
  const inputServer = path.resolve(scriptDir, 'server.js')
45
65
 
46
66
  const inputFiles = [inputClient, inputServer]
47
67
 
48
- if (!fs.existsSync(appSourcePath)) {
49
- throw new Error(`[@ossy/app][build] Source path does not exist: ${appSourcePath}`);
50
- }
68
+ printBuildOverview({ pagesSourcePath: effectivePagesSource, apiSourcePath, configPath, isPageFiles, pageFiles: isPageFiles ? pageFiles : [] });
51
69
 
52
70
  if (!fs.existsSync(apiSourcePath)) {
53
71
  apiSourcePath = path.resolve(scriptDir, 'api.js')
@@ -57,9 +75,9 @@ export const dev = async (cliArgs) => {
57
75
  middlewareSourcePath = path.resolve(scriptDir, 'middleware.js')
58
76
  }
59
77
 
60
- if (fs.existsSync(configPath)) {
61
- inputFiles.push(configPath)
62
- }
78
+ const configSourcePath = fs.existsSync(configPath)
79
+ ? configPath
80
+ : path.resolve(scriptDir, 'default-config.js')
63
81
 
64
82
  const inputOptions = {
65
83
  input: inputFiles,
@@ -69,7 +87,12 @@ export const dev = async (cliArgs) => {
69
87
  replace({
70
88
  preventAssignment: true,
71
89
  delimiters: ['%%', '%%'],
72
- '@ossy/app/source-file': appSourcePath,
90
+ '@ossy/app/source-file': path.resolve(scriptDir, 'default-app.jsx'),
91
+ }),
92
+ replace({
93
+ preventAssignment: true,
94
+ delimiters: ['%%', '%%'],
95
+ '@ossy/pages/source-file': effectivePagesSource,
73
96
  }),
74
97
  replace({
75
98
  preventAssignment: true,
@@ -81,6 +104,11 @@ export const dev = async (cliArgs) => {
81
104
  delimiters: ['%%', '%%'],
82
105
  '@ossy/middleware/source-file': middlewareSourcePath,
83
106
  }),
107
+ replace({
108
+ preventAssignment: true,
109
+ delimiters: ['%%', '%%'],
110
+ '@ossy/config/source-file': configSourcePath,
111
+ }),
84
112
  replace({
85
113
  preventAssignment: true,
86
114
  'process.env.NODE_ENV': JSON.stringify('development')
@@ -173,4 +201,16 @@ export const dev = async (cliArgs) => {
173
201
  restartServer()
174
202
  }
175
203
  })
204
+
205
+ if (isPageFiles) {
206
+ fs.watch(srcDir, { recursive: true }, (eventType, filename) => {
207
+ if (filename && /\.page\.(jsx?|tsx?)$/.test(filename)) {
208
+ const files = discoverPageFiles(srcDir)
209
+ if (files.length > 0) {
210
+ const generatedPath = path.join(cwd, '.ossy-pages.generated.jsx')
211
+ fs.writeFileSync(generatedPath, generatePagesModule(files, cwd, pagesOpt))
212
+ }
213
+ }
214
+ })
215
+ }
176
216
  };
@@ -37,7 +37,7 @@ export function ProxyInternal() {
37
37
  }
38
38
 
39
39
  if (req.originalUrl.startsWith('/@ossy/users/me/app-settings') && req.method === 'GET') {
40
- LogService.Info({ message: `[UsersService][HandleUserAppSettings] METHOD ${req.method}` })
40
+ console.log(`[@ossy/app][proxy] GET /@ossy/users/me/app-settings`)
41
41
  const userSettings = JSON.parse(req.signedCookies?.['x-ossy-user-settings'] || '{}')
42
42
  res.status(200)
43
43
  res.json(userSettings)
package/cli/server.js CHANGED
@@ -11,6 +11,9 @@ import cookieParser from 'cookie-parser'
11
11
  import App from '%%@ossy/app/source-file%%'
12
12
  import ApiRoutes from '%%@ossy/api/source-file%%'
13
13
  import Middleware from '%%@ossy/middleware/source-file%%'
14
+ import configModule from '%%@ossy/config/source-file%%'
15
+
16
+ const buildTimeConfig = configModule?.default ?? configModule ?? {}
14
17
 
15
18
  const app = express();
16
19
 
@@ -108,14 +111,18 @@ app.all('*all', (req, res) => {
108
111
  if (apiRoute) {
109
112
  console.log(`[@ossy/app][server] Handling API route: ${pathname}`)
110
113
  apiRoute.handle(req, res)
114
+ return
111
115
  }
112
116
 
113
117
  const userAppSettings = req.userAppSettings || {}
114
118
 
115
119
  const appConfig = {
120
+ ...buildTimeConfig,
116
121
  url: req.url,
117
- theme: userAppSettings.theme || 'light',
122
+ theme: userAppSettings.theme || buildTimeConfig.theme || 'light',
118
123
  isAuthenticated: req.isAuthenticated || false,
124
+ workspaceId: userAppSettings.workspaceId || buildTimeConfig.workspaceId,
125
+ apiUrl: buildTimeConfig.apiUrl,
119
126
  }
120
127
 
121
128
  renderToString(App, appConfig)
package/package.json CHANGED
@@ -1,18 +1,23 @@
1
1
  {
2
2
  "name": "@ossy/app",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "",
5
5
  "source": "./src/index.js",
6
6
  "main": "./src/index.js",
7
- "bin": "./cli/index.js",
8
7
  "type": "module",
9
8
  "scripts": {
10
9
  "build": "echo Building not required",
11
- "test": "echo 'No test specified'"
10
+ "test": "node --experimental-vm-modules ../node_modules/jest/bin/jest.js --verbose",
11
+ "typecheck": "tsc --noEmit"
12
12
  },
13
13
  "keywords": [],
14
14
  "author": "Ossy <yourfriends@ossy.se> (https://ossy.se)",
15
15
  "license": "MIT",
16
+ "devDependencies": {
17
+ "@jest/globals": "^30.2.0",
18
+ "jest": "^30.2.0",
19
+ "typescript": "^5.9.3"
20
+ },
16
21
  "peerDependencies": {
17
22
  "@babel/cli": "^7.26.4",
18
23
  "@babel/core": "^7.26.0",
@@ -22,7 +27,7 @@
22
27
  "@babel/register": "^7.25.9",
23
28
  "@ossy/connected-components": ">=0.5.0 <1.0.0",
24
29
  "@ossy/design-system": ">=0.5.0 <1.0.0",
25
- "@ossy/design-system-extras": ">=0.5.0 <1.0.0",
30
+ "@ossy/pages": ">=0.5.0 <1.0.0",
26
31
  "@ossy/resource-templates": ">=0.5.0 <1.0.0",
27
32
  "@ossy/router": ">=0.5.0 <1.0.0",
28
33
  "@ossy/router-react": ">=0.5.0 <1.0.0",
@@ -56,7 +61,9 @@
56
61
  },
57
62
  "files": [
58
63
  "/cli",
59
- "README.md"
64
+ "/src",
65
+ "README.md",
66
+ "tsconfig.json"
60
67
  ],
61
- "gitHead": "eec27800542ed59264caf788326e525a2ee77d63"
68
+ "gitHead": "3019a8da590435ba6f10d7d861addeba011baf5d"
62
69
  }
package/src/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { build } from '../cli/build.js'
2
+ export { dev } from '../cli/dev.js'
package/tsconfig.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "node",
6
+ "lib": ["ES2022"],
7
+ "allowJs": true,
8
+ "checkJs": false,
9
+ "strict": true,
10
+ "skipLibCheck": true,
11
+ "noEmit": true,
12
+ "esModuleInterop": true,
13
+ "resolveJsonModule": true,
14
+ "isolatedModules": true,
15
+ "forceConsistentCasingInFileNames": true
16
+ },
17
+ "include": ["cli/**/*.js", "src/**/*.js"],
18
+ "exclude": ["node_modules", "build", "**/__tests__/**"]
19
+ }
package/cli/index.js DELETED
@@ -1,21 +0,0 @@
1
- #!/usr/bin/env node
2
- /* eslint-disable global-require, no-unused-vars */
3
- import { build } from './build.js'
4
- import { dev } from './dev.js'
5
-
6
- const [_, __, command, ...restArgs] = process.argv
7
-
8
- if (!command) {
9
- console.error({ message: '[@ossy/app] No command provided' })
10
- }
11
-
12
- const commandHandler = {
13
- 'build': build,
14
- 'dev': dev,
15
- }[command]
16
-
17
- if (!commandHandler) {
18
- console.error({ message: `[@ossy/app] Unknown command: ${command}` })
19
- }
20
-
21
- commandHandler(restArgs)