@adonisjs/assembler 6.1.3-6 → 6.1.3-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/src/dev_server.ts DELETED
@@ -1,307 +0,0 @@
1
- /*
2
- * @adonisjs/assembler
3
- *
4
- * (c) AdonisJS
5
- *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
8
- */
9
-
10
- import picomatch from 'picomatch'
11
- import type tsStatic from 'typescript'
12
- import { type ExecaChildProcess } from 'execa'
13
- import { cliui, type Logger } from '@poppinss/cliui'
14
- import type { Watcher } from '@poppinss/chokidar-ts'
15
-
16
- import type { DevServerOptions } from './types.js'
17
- import { AssetsDevServer } from './assets_dev_server.js'
18
- import { getPort, isDotEnvFile, isRcFile, runNode, watch } from './helpers.js'
19
-
20
- /**
21
- * Instance of CLIUI
22
- */
23
- const ui = cliui()
24
-
25
- /**
26
- * Exposes the API to start the development. Optionally, the watch API can be
27
- * used to watch for file changes and restart the development server.
28
- *
29
- * The Dev server performs the following actions
30
- *
31
- * - Assigns a random PORT, when PORT inside .env file is in use
32
- * - Uses tsconfig.json file to collect a list of files to watch.
33
- * - Uses metaFiles from .adonisrc.json file to collect a list of files to watch.
34
- * - Restart HTTP server on every file change.
35
- */
36
- export class DevServer {
37
- #cwd: URL
38
- #logger = ui.logger
39
- #options: DevServerOptions
40
- #isWatching: boolean = false
41
- #scriptFile: string = 'bin/server.js'
42
- #isMetaFileWithReloadsEnabled: picomatch.Matcher
43
- #isMetaFileWithReloadsDisabled: picomatch.Matcher
44
-
45
- #onError?: (error: any) => any
46
- #onClose?: (exitCode: number) => any
47
-
48
- #httpServer?: ExecaChildProcess<string>
49
- #watcher?: ReturnType<Watcher['watch']>
50
- #assetsServer?: AssetsDevServer
51
-
52
- /**
53
- * Getting reference to colors library from logger
54
- */
55
- get #colors() {
56
- return this.#logger.getColors()
57
- }
58
-
59
- constructor(cwd: URL, options: DevServerOptions) {
60
- this.#cwd = cwd
61
- this.#options = options
62
-
63
- this.#isMetaFileWithReloadsEnabled = picomatch(
64
- (this.#options.metaFiles || [])
65
- .filter(({ reloadServer }) => reloadServer === true)
66
- .map(({ pattern }) => pattern)
67
- )
68
-
69
- this.#isMetaFileWithReloadsDisabled = picomatch(
70
- (this.#options.metaFiles || [])
71
- .filter(({ reloadServer }) => reloadServer !== true)
72
- .map(({ pattern }) => pattern)
73
- )
74
- }
75
-
76
- /**
77
- * Inspect if child process message is from AdonisJS HTTP server
78
- */
79
- #isAdonisJSReadyMessage(
80
- message: unknown
81
- ): message is { isAdonisJS: true; environment: 'web'; port: number; host: string } {
82
- return (
83
- message !== null &&
84
- typeof message === 'object' &&
85
- 'isAdonisJS' in message &&
86
- 'environment' in message &&
87
- message.environment === 'web'
88
- )
89
- }
90
-
91
- /**
92
- * Conditionally clear the terminal screen
93
- */
94
- #clearScreen() {
95
- if (this.#options.clearScreen) {
96
- process.stdout.write('\u001Bc')
97
- }
98
- }
99
-
100
- /**
101
- * Starts the HTTP server
102
- */
103
- #startHTTPServer(port: string, mode: 'blocking' | 'nonblocking') {
104
- this.#httpServer = runNode(this.#cwd, {
105
- script: this.#scriptFile,
106
- env: { PORT: port, ...this.#options.env },
107
- nodeArgs: this.#options.nodeArgs,
108
- scriptArgs: this.#options.scriptArgs,
109
- })
110
-
111
- this.#httpServer.on('message', (message) => {
112
- if (this.#isAdonisJSReadyMessage(message)) {
113
- ui.sticker()
114
- .useColors(this.#colors)
115
- .useRenderer(this.#logger.getRenderer())
116
- .add(`Server address: ${this.#colors.cyan(`http://${message.host}:${message.port}`)}`)
117
- .add(
118
- `File system watcher: ${this.#colors.cyan(
119
- `${this.#isWatching ? 'enabled' : 'disabled'}`
120
- )}`
121
- )
122
- .render()
123
- }
124
- })
125
-
126
- this.#httpServer
127
- .then((result) => {
128
- this.#logger.warning(`underlying HTTP server closed with status code "${result.exitCode}"`)
129
- if (mode === 'nonblocking') {
130
- this.#onClose?.(result.exitCode)
131
- this.#watcher?.close()
132
- this.#assetsServer?.stop()
133
- }
134
- })
135
- .catch((error) => {
136
- this.#logger.warning('unable to connect to underlying HTTP server process')
137
- this.#logger.fatal(error)
138
- this.#onError?.(error)
139
- this.#watcher?.close()
140
- this.#assetsServer?.stop()
141
- })
142
- }
143
-
144
- /**
145
- * Starts the assets server
146
- */
147
- #startAssetsServer() {
148
- this.#assetsServer = new AssetsDevServer(this.#cwd, this.#options.assets)
149
- this.#assetsServer.setLogger(this.#logger)
150
- this.#assetsServer.start()
151
- }
152
-
153
- /**
154
- * Restarts the HTTP server
155
- */
156
- #restartHTTPServer(port: string) {
157
- if (this.#httpServer) {
158
- this.#httpServer.removeAllListeners()
159
- this.#httpServer.kill('SIGKILL')
160
- }
161
-
162
- this.#startHTTPServer(port, 'blocking')
163
- }
164
-
165
- /**
166
- * Handles a non TypeScript file change
167
- */
168
- #handleFileChange(action: string, port: string, relativePath: string) {
169
- if (isDotEnvFile(relativePath) || isRcFile(relativePath)) {
170
- this.#clearScreen()
171
- this.#logger.log(`${this.#colors.green(action)} ${relativePath}`)
172
- this.#restartHTTPServer(port)
173
- return
174
- }
175
-
176
- if (this.#isMetaFileWithReloadsEnabled(relativePath)) {
177
- this.#clearScreen()
178
- this.#logger.log(`${this.#colors.green(action)} ${relativePath}`)
179
- this.#restartHTTPServer(port)
180
- return
181
- }
182
-
183
- if (this.#isMetaFileWithReloadsDisabled(relativePath)) {
184
- this.#clearScreen()
185
- this.#logger.log(`${this.#colors.green(action)} ${relativePath}`)
186
- }
187
- }
188
-
189
- /**
190
- * Handles TypeScript source file change
191
- */
192
- #handleSourceFileChange(action: string, port: string, relativePath: string) {
193
- this.#clearScreen()
194
- this.#logger.log(`${this.#colors.green(action)} ${relativePath}`)
195
- this.#restartHTTPServer(port)
196
- }
197
-
198
- /**
199
- * Set a custom CLI UI logger
200
- */
201
- setLogger(logger: Logger) {
202
- this.#logger = logger
203
- this.#assetsServer?.setLogger(logger)
204
- return this
205
- }
206
-
207
- /**
208
- * Add listener to get notified when dev server is
209
- * closed
210
- */
211
- onClose(callback: (exitCode: number) => any): this {
212
- this.#onClose = callback
213
- return this
214
- }
215
-
216
- /**
217
- * Add listener to get notified when dev server exists
218
- * with an error
219
- */
220
- onError(callback: (error: any) => any): this {
221
- this.#onError = callback
222
- return this
223
- }
224
-
225
- /**
226
- * Start the development server
227
- */
228
- async start() {
229
- this.#clearScreen()
230
- this.#logger.info('starting HTTP server...')
231
- this.#startHTTPServer(String(await getPort(this.#cwd)), 'nonblocking')
232
- this.#startAssetsServer()
233
- }
234
-
235
- /**
236
- * Start the development server in watch mode
237
- */
238
- async startAndWatch(ts: typeof tsStatic, options?: { poll: boolean }) {
239
- const port = String(await getPort(this.#cwd))
240
- this.#isWatching = true
241
-
242
- this.#clearScreen()
243
-
244
- this.#logger.info('starting HTTP server...')
245
- this.#startHTTPServer(port, 'blocking')
246
-
247
- this.#startAssetsServer()
248
-
249
- /**
250
- * Create watcher using tsconfig.json file
251
- */
252
- const output = watch(this.#cwd, ts, options || {})
253
- if (!output) {
254
- this.#onClose?.(1)
255
- return
256
- }
257
-
258
- /**
259
- * Storing reference to watcher, so that we can close it
260
- * when HTTP server exists with error
261
- */
262
- this.#watcher = output.chokidar
263
-
264
- /**
265
- * Notify the watcher is ready
266
- */
267
- output.watcher.on('watcher:ready', () => {
268
- this.#logger.info('watching file system for changes...')
269
- })
270
-
271
- /**
272
- * Cleanup when watcher dies
273
- */
274
- output.chokidar.on('error', (error) => {
275
- this.#logger.warning('file system watcher failure')
276
- this.#logger.fatal(error)
277
- this.#onError?.(error)
278
- output.chokidar.close()
279
- })
280
-
281
- /**
282
- * Changes in TypeScript source file
283
- */
284
- output.watcher.on('source:add', ({ relativePath }) =>
285
- this.#handleSourceFileChange('add', port, relativePath)
286
- )
287
- output.watcher.on('source:change', ({ relativePath }) =>
288
- this.#handleSourceFileChange('update', port, relativePath)
289
- )
290
- output.watcher.on('source:unlink', ({ relativePath }) =>
291
- this.#handleSourceFileChange('delete', port, relativePath)
292
- )
293
-
294
- /**
295
- * Changes in non-TypeScript source files
296
- */
297
- output.watcher.on('add', ({ relativePath }) =>
298
- this.#handleFileChange('add', port, relativePath)
299
- )
300
- output.watcher.on('change', ({ relativePath }) =>
301
- this.#handleFileChange('update', port, relativePath)
302
- )
303
- output.watcher.on('unlink', ({ relativePath }) =>
304
- this.#handleFileChange('delete', port, relativePath)
305
- )
306
- }
307
- }
package/src/helpers.ts DELETED
@@ -1,162 +0,0 @@
1
- /*
2
- * @adonisjs/assembler
3
- *
4
- * (c) AdonisJS
5
- *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
8
- */
9
-
10
- import getRandomPort from 'get-port'
11
- import type tsStatic from 'typescript'
12
- import { fileURLToPath } from 'node:url'
13
- import { execaNode, execa } from 'execa'
14
- import { EnvLoader, EnvParser } from '@adonisjs/env'
15
- import { ConfigParser, Watcher } from '@poppinss/chokidar-ts'
16
-
17
- import type { RunOptions, WatchOptions } from './types.js'
18
-
19
- /**
20
- * Default set of args to pass in order to run TypeScript
21
- * source. Used by "run" and "runNode" scripts
22
- */
23
- const DEFAULT_NODE_ARGS = [
24
- // Use ts-node/esm loader. The project must install it
25
- '--loader=ts-node/esm',
26
- // Disable annonying warnings
27
- '--no-warnings',
28
- // Enable expiremental meta resolve for cases where someone uses magic import string
29
- '--experimental-import-meta-resolve',
30
- ]
31
-
32
- /**
33
- * Parses tsconfig.json and prints errors using typescript compiler
34
- * host
35
- */
36
- export function parseConfig(cwd: string | URL, ts: typeof tsStatic) {
37
- const { config, error } = new ConfigParser(cwd, 'tsconfig.json', ts).parse()
38
- if (error) {
39
- const compilerHost = ts.createCompilerHost({})
40
- console.log(ts.formatDiagnosticsWithColorAndContext([error], compilerHost))
41
- return
42
- }
43
-
44
- if (config!.errors.length) {
45
- const compilerHost = ts.createCompilerHost({})
46
- console.log(ts.formatDiagnosticsWithColorAndContext(config!.errors, compilerHost))
47
- return
48
- }
49
-
50
- return config
51
- }
52
-
53
- /**
54
- * Runs a Node.js script as a child process and inherits the stdio streams
55
- */
56
- export function runNode(cwd: string | URL, options: RunOptions) {
57
- const childProcess = execaNode(options.script, options.scriptArgs, {
58
- nodeOptions: DEFAULT_NODE_ARGS.concat(options.nodeArgs),
59
- preferLocal: true,
60
- windowsHide: false,
61
- localDir: cwd,
62
- cwd,
63
- buffer: false,
64
- stdio: options.stdio || 'inherit',
65
- env: {
66
- ...(options.stdio === 'pipe' ? { FORCE_COLOR: 'true' } : {}),
67
- ...options.env,
68
- },
69
- })
70
-
71
- return childProcess
72
- }
73
-
74
- /**
75
- * Runs a script as a child process and inherits the stdio streams
76
- */
77
- export function run(cwd: string | URL, options: Omit<RunOptions, 'nodeArgs'>) {
78
- const childProcess = execa(options.script, options.scriptArgs, {
79
- preferLocal: true,
80
- windowsHide: false,
81
- localDir: cwd,
82
- cwd,
83
- buffer: false,
84
- stdio: options.stdio || 'inherit',
85
- env: {
86
- ...(options.stdio === 'pipe' ? { FORCE_COLOR: 'true' } : {}),
87
- ...options.env,
88
- },
89
- })
90
-
91
- return childProcess
92
- }
93
-
94
- /**
95
- * Watches the file system using tsconfig file
96
- */
97
- export function watch(cwd: string | URL, ts: typeof tsStatic, options: WatchOptions) {
98
- const config = parseConfig(cwd, ts)
99
- if (!config) {
100
- return
101
- }
102
-
103
- const watcher = new Watcher(typeof cwd === 'string' ? cwd : fileURLToPath(cwd), config!)
104
- const chokidar = watcher.watch(['.'], { usePolling: options.poll })
105
- return { watcher, chokidar }
106
- }
107
-
108
- /**
109
- * Check if file is an .env file
110
- */
111
- export function isDotEnvFile(filePath: string) {
112
- if (filePath === '.env') {
113
- return true
114
- }
115
-
116
- return filePath.includes('.env.')
117
- }
118
-
119
- /**
120
- * Check if file is .adonisrc.json file
121
- */
122
- export function isRcFile(filePath: string) {
123
- return filePath === '.adonisrc.json'
124
- }
125
-
126
- /**
127
- * Returns the port to use after inspect the dot-env files inside
128
- * a given directory.
129
- *
130
- * A random port is used when the specified port is in use. Following
131
- * is the logic for finding a specified port.
132
- *
133
- * - The "process.env.PORT" value is used if exists.
134
- * - The dot-env files are loaded using the "EnvLoader" and the PORT
135
- * value is by iterating over all the loaded files. The iteration
136
- * stops after first find.
137
- */
138
- export async function getPort(cwd: URL): Promise<number> {
139
- /**
140
- * Use existing port if exists
141
- */
142
- if (process.env.PORT) {
143
- return getRandomPort({ port: Number(process.env.PORT) })
144
- }
145
-
146
- /**
147
- * Loop over files and use the port from their contents. Stops
148
- * after first match
149
- */
150
- const files = await new EnvLoader(cwd).load()
151
- for (let file of files) {
152
- const envVariables = new EnvParser(file.contents).parse()
153
- if (envVariables.PORT) {
154
- return getRandomPort({ port: Number(envVariables.PORT) })
155
- }
156
- }
157
-
158
- /**
159
- * Use 3333 as the port
160
- */
161
- return getRandomPort({ port: 3333 })
162
- }