@ossy/app 0.7.16 → 0.8.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.
Files changed (3) hide show
  1. package/cli/dev.js +75 -36
  2. package/cli/server.js +36 -1
  3. package/package.json +2 -2
package/cli/dev.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import path from 'path';
2
2
  import url from 'url';
3
3
  import fs from 'fs';
4
- import { rollup } from 'rollup';
4
+ import { watch } from 'rollup';
5
5
  import babel from '@rollup/plugin-babel';
6
6
  import { nodeResolve as resolveDependencies } from '@rollup/plugin-node-resolve'
7
7
  import resolveCommonJsDependencies from '@rollup/plugin-commonjs'
@@ -14,10 +14,11 @@ import copy from 'rollup-plugin-copy';
14
14
  import replace from '@rollup/plugin-replace';
15
15
  import remove from 'rollup-plugin-delete';
16
16
  import arg from 'arg'
17
+ import { spawn } from 'node:child_process'
17
18
  // import inject from '@rollup/plugin-inject'
18
19
 
19
20
  export const dev = async (cliArgs) => {
20
- console.log('[@ossy/app][build] Starting...')
21
+ console.log('[@ossy/app][dev] Starting...')
21
22
 
22
23
  const options = arg({
23
24
  '--source': String,
@@ -28,12 +29,12 @@ export const dev = async (cliArgs) => {
28
29
 
29
30
  '--config': String,
30
31
  '-c': '--config',
31
- }, { argv: cliArgs })
32
+ }, { argv: cliArgs, permissive: true })
32
33
 
33
34
 
34
35
  const appSourcePath = path.resolve(options['--source'] || 'src/App.jsx');
35
- let apiSourcePath = path.resolve(options['--api-source'] || 'src/Api.js');
36
- let middlewareSourcePath = path.resolve(options['--middleware-source'] || 'src/Middleware.js');
36
+ let apiSourcePath = path.resolve(options['--api-source'] || 'src/api.js');
37
+ let middlewareSourcePath = path.resolve(options['--middleware-source'] || 'src/middleware.js');
37
38
  const configPath = path.resolve(options['--config'] || 'src/config.js');
38
39
  const buildPath = path.resolve(options['--destination'] || 'build');
39
40
  const publicDir = path.resolve('public')
@@ -49,11 +50,11 @@ export const dev = async (cliArgs) => {
49
50
  }
50
51
 
51
52
  if (!fs.existsSync(apiSourcePath)) {
52
- apiSourcePath = path.resolve(scriptDir, 'Api.js')
53
+ apiSourcePath = path.resolve(scriptDir, 'api.js')
53
54
  }
54
55
 
55
56
  if (!fs.existsSync(middlewareSourcePath)) {
56
- apiSourcePath = path.resolve(scriptDir, 'Middleware.js')
57
+ middlewareSourcePath = path.resolve(scriptDir, 'middleware.js')
57
58
  }
58
59
 
59
60
  if (fs.existsSync(configPath)) {
@@ -78,11 +79,11 @@ export const dev = async (cliArgs) => {
78
79
  replace({
79
80
  preventAssignment: true,
80
81
  delimiters: ['%%', '%%'],
81
- '@ossy/middleware/source-file': apiSourcePath,
82
+ '@ossy/middleware/source-file': middlewareSourcePath,
82
83
  }),
83
84
  replace({
84
85
  preventAssignment: true,
85
- 'process.env.NODE_ENV': JSON.stringify('production')
86
+ 'process.env.NODE_ENV': JSON.stringify('development')
86
87
  }),
87
88
  json(),
88
89
  // removeOwnPeerDependencies(),
@@ -105,33 +106,71 @@ export const dev = async (cliArgs) => {
105
106
  ],
106
107
  };
107
108
 
108
- const outputOptions = [
109
- {
110
- dir: 'build',
111
- // preserveModules: true,
112
- entryFileNames: ({ name }) => {
113
- if (name === 'server') {
114
- return '[name].js'
115
- } else if (name === 'Api') {
116
- return '[name].js'
117
- } else if (name === 'client') {
118
- return 'public/static/main.js'
119
- } else if (name === 'config') {
120
- return 'public/static/[name].js'
121
- } else {
122
- return 'public/static/[name].js'
123
- }
124
- },
125
- chunkFileNames: 'public/static/[name]-[hash].js',
126
- format: 'esm',
127
- }
128
- ];
129
-
130
- const bundle = await rollup.watch(inputOptions);
131
-
132
- for (const options of outputOptions) {
133
- await bundle.write(options);
109
+ const outputOptions = {
110
+ dir: 'build',
111
+ // preserveModules: true,
112
+ entryFileNames: ({ name }) => {
113
+ const serverFileNames = ['server', 'api', 'middleware']
114
+ if (serverFileNames.includes(name)) return '[name].js'
115
+ if (name === 'client') return 'public/static/main.js'
116
+ if (name === 'config') return 'public/static/[name].js'
117
+ return 'public/static/[name].js'
118
+ },
119
+ chunkFileNames: 'public/static/[name]-[hash].js',
120
+ format: 'esm',
134
121
  }
135
122
 
136
- console.log('[@ossy/app][build] Finished');
123
+ let serverProcess = null
124
+ const startServer = () => {
125
+ if (serverProcess) return
126
+ serverProcess = spawn(process.execPath, [path.resolve(buildPath, 'server.js'), ...process.argv.slice(3)], {
127
+ stdio: 'inherit',
128
+ env: {
129
+ ...process.env,
130
+ OSSY_DEV_RELOAD: '1',
131
+ NODE_ENV: 'development',
132
+ },
133
+ })
134
+ serverProcess.on('exit', () => {
135
+ serverProcess = null
136
+ })
137
+ }
138
+
139
+ const restartServer = () => {
140
+ if (!serverProcess) return startServer()
141
+ serverProcess.kill('SIGTERM')
142
+ serverProcess = null
143
+ startServer()
144
+ }
145
+
146
+ const triggerReload = async () => {
147
+ const port = process.env.PORT || '3000'
148
+ try {
149
+ await fetch(`http://localhost:${port}/__ossy_reload`, { method: 'POST' })
150
+ } catch {
151
+ // server might not be up yet
152
+ }
153
+ }
154
+
155
+ const watcher = watch({
156
+ ...inputOptions,
157
+ output: outputOptions,
158
+ watch: { clearScreen: false },
159
+ })
160
+
161
+ watcher.on('event', async (event) => {
162
+ if (event.code === 'BUNDLE_START') {
163
+ console.log('[@ossy/app][dev] Building...')
164
+ }
165
+ if (event.code === 'ERROR') {
166
+ console.error('[@ossy/app][dev] Build error', event.error)
167
+ }
168
+ if (event.code === 'BUNDLE_END') {
169
+ console.log(`[@ossy/app][dev] Built in ${event.duration}ms`)
170
+ }
171
+ if (event.code === 'END') {
172
+ await triggerReload()
173
+ restartServer()
174
+ }
175
+ })
137
176
  };
package/cli/server.js CHANGED
@@ -17,6 +17,9 @@ const app = express();
17
17
  const currentDir = path.dirname(url.fileURLToPath(import.meta.url))
18
18
  const ROOT_PATH = path.resolve(currentDir, 'public')
19
19
 
20
+ const isDevReloadEnabled = process.env.OSSY_DEV_RELOAD === '1'
21
+ const reloadClients = new Set()
22
+
20
23
  function parsePortFromArgv(argv) {
21
24
  // Supports: --port 4000, --port=4000, -p 4000
22
25
  const idx = argv.findIndex(a => a === '--port' || a === '-p')
@@ -42,6 +45,34 @@ if (Middleware !== undefined) {
42
45
  console.log(`[@ossy/app][server] ${Middleware?.length || 0} custom middleware loaded`)
43
46
  }
44
47
 
48
+ if (isDevReloadEnabled) {
49
+ app.get('/__ossy_reload', (req, res) => {
50
+ res.status(200)
51
+ res.setHeader('Content-Type', 'text/event-stream')
52
+ res.setHeader('Cache-Control', 'no-cache')
53
+ res.setHeader('Connection', 'keep-alive')
54
+ res.flushHeaders?.()
55
+
56
+ res.write('event: connected\ndata: ok\n\n')
57
+ reloadClients.add(res)
58
+
59
+ req.on('close', () => {
60
+ reloadClients.delete(res)
61
+ })
62
+ })
63
+
64
+ app.post('/__ossy_reload', (req, res) => {
65
+ for (const client of reloadClients) {
66
+ try {
67
+ client.write('event: reload\ndata: now\n\n')
68
+ } catch {
69
+ // ignore broken connections
70
+ }
71
+ }
72
+ res.status(204).end()
73
+ })
74
+ }
75
+
45
76
  const middleware = [
46
77
  morgan('tiny'),
47
78
  express.json({ strict: false }),
@@ -99,8 +130,12 @@ app.listen(port, () => {
99
130
 
100
131
  async function renderToString(App, config) {
101
132
 
133
+ const devReloadScript = isDevReloadEnabled
134
+ ? `(function(){try{var es=new EventSource('/__ossy_reload');es.addEventListener('reload',function(){location.reload();});}catch(e){}})();`
135
+ : ``
136
+
102
137
  const { prelude } = await prerenderToNodeStream(createElement(App, config), {
103
- bootstrapScriptContent: `window.__INITIAL_APP_CONFIG__ = ${JSON.stringify(config)};`,
138
+ bootstrapScriptContent: `window.__INITIAL_APP_CONFIG__ = ${JSON.stringify(config)};${devReloadScript}`,
104
139
  bootstrapModules: ['/static/main.js']
105
140
  });
106
141
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ossy/app",
3
- "version": "0.7.16",
3
+ "version": "0.8.0",
4
4
  "description": "",
5
5
  "source": "./src/index.js",
6
6
  "main": "./src/index.js",
@@ -58,5 +58,5 @@
58
58
  "/cli",
59
59
  "README.md"
60
60
  ],
61
- "gitHead": "2a788e4868aa7ed2b0bc9869533f926d4cf6828a"
61
+ "gitHead": "4aa6bd438e39babbb136f388aa7b5df9ba701144"
62
62
  }