@adonisjs/assembler 6.1.3-3 → 6.1.3-4
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/LICENSE.md +1 -1
- package/README.md +3 -3
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -0
- package/build/src/bundler.d.ts +1 -0
- package/build/src/bundler.d.ts.map +1 -0
- package/build/src/dev_server.d.ts +1 -0
- package/build/src/dev_server.d.ts.map +1 -0
- package/build/src/parse_config.d.ts +1 -0
- package/build/src/parse_config.d.ts.map +1 -0
- package/build/src/run.d.ts +1 -0
- package/build/src/run.d.ts.map +1 -0
- package/build/src/types.d.ts +1 -0
- package/build/src/types.d.ts.map +1 -0
- package/build/src/watch.d.ts +1 -0
- package/build/src/watch.d.ts.map +1 -0
- package/index.ts +11 -0
- package/package.json +14 -11
- package/src/bundler.ts +268 -0
- package/src/dev_server.ts +470 -0
- package/src/parse_config.ts +32 -0
- package/src/run.ts +65 -0
- package/src/types.ts +69 -0
- package/src/watch.ts +29 -0
package/LICENSE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2023
|
|
3
|
+
Copyright (c) 2023 Harminder Virk
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
6
|
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<br />
|
|
4
4
|
|
|
5
|
-
[![gh-workflow-image]][gh-workflow-url] [![npm-image]][npm-url] ![][typescript-image] [![license-image]][license-url] [![
|
|
5
|
+
[![gh-workflow-image]][gh-workflow-url] [![npm-image]][npm-url] ![][typescript-image] [![license-image]][license-url] [![snyk-image]][snyk-url]
|
|
6
6
|
|
|
7
7
|
## Introduction
|
|
8
8
|
Assembler exports the API for starting the **AdonisJS development server**, **building project for production** and **running tests** in watch mode. Assembler must be used during development only.
|
|
@@ -32,5 +32,5 @@ AdonisJS Assembler is open-sourced software licensed under the [MIT license](LIC
|
|
|
32
32
|
[license-url]: LICENSE.md
|
|
33
33
|
[license-image]: https://img.shields.io/github/license/adonisjs/ace?style=for-the-badge
|
|
34
34
|
|
|
35
|
-
[
|
|
36
|
-
[
|
|
35
|
+
[snyk-image]: https://img.shields.io/snyk/vulnerabilities/github/adonisjs/assembler?label=snyk%20Vulnerabilities&style=for-the-badge
|
|
36
|
+
[snyk-url]: https://snyk.io/test/github/adonisjs/assembler?targetFile=package.json "snyk"
|
package/build/index.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA"}
|
package/build/src/bundler.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundler.d.ts","sourceRoot":"","sources":["../../src/bundler.ts"],"names":[],"mappings":";AAYA,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAA;AAGtC,OAAO,EAAS,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAIpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAUhD,qBAAa,OAAO;;gBAcN,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,QAAQ,EAAE,OAAO,EAAE,cAAc;IAkIlE,SAAS,CAAC,MAAM,EAAE,MAAM;IAQlB,MAAM,CACV,WAAW,GAAE,OAAc,EAC3B,MAAM,GAAE,KAAK,GAAG,MAAM,GAAG,MAAc,GACtC,OAAO,CAAC,OAAO,CAAC;CAmFpB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev_server.d.ts","sourceRoot":"","sources":["../../src/dev_server.ts"],"names":[],"mappings":";AAWA,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAA;AAGtC,OAAO,EAAS,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAKpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAkBlD,qBAAa,SAAS;;gBAqBR,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,gBAAgB;IAyP/C,SAAS,CAAC,MAAM,EAAE,MAAM;IASxB,OAAO,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,GAAG,GAAG,IAAI;IASlD,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,GAAG,IAAI;IAQtC,KAAK;IAWL,aAAa,CAAC,EAAE,EAAE,OAAO,QAAQ,EAAE,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE;CA6HrE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse_config.d.ts","sourceRoot":"","sources":["../../src/parse_config.ts"],"names":[],"mappings":";AASA,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAA;AAOtC,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,EAAE,EAAE,OAAO,QAAQ,0CAejE"}
|
package/build/src/run.d.ts
CHANGED
|
@@ -2,3 +2,4 @@
|
|
|
2
2
|
import type { RunOptions } from './types.js';
|
|
3
3
|
export declare function runNode(cwd: string | URL, options: RunOptions): import("execa").ExecaChildProcess<string>;
|
|
4
4
|
export declare function run(cwd: string | URL, options: Omit<RunOptions, 'nodeArgs'>): import("execa").ExecaChildProcess<string>;
|
|
5
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/run.ts"],"names":[],"mappings":";AAUA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAkB5C,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,OAAO,EAAE,UAAU,6CAgB7D;AAKD,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,6CAe3E"}
|
package/build/src/types.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":";AAYA,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CACxB,CAAA;AAKD,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,CAAC,EAAE,OAAO,CAAA;CACf,CAAA;AAKD,MAAM,MAAM,QAAQ,GAAG;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,OAAO,CAAA;CACtB,CAAA;AAKD,MAAM,MAAM,oBAAoB,GAC5B;IACE,KAAK,EAAE,KAAK,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAA;CACb,GACD;IACE,KAAK,EAAE,IAAI,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA;AAKL,MAAM,MAAM,gBAAgB,GAAG;IAC7B,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;IACvB,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAA;IACtB,MAAM,CAAC,EAAE,oBAAoB,CAAA;CAC9B,CAAA;AAKD,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAA;IACtB,MAAM,CAAC,EAAE,oBAAoB,CAAA;CAC9B,CAAA"}
|
package/build/src/watch.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/watch.ts"],"names":[],"mappings":";AASA,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAA;AAEtC,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAE/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAM9C,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,EAAE,EAAE,OAAO,QAAQ,EAAE,OAAO,EAAE,YAAY;;;cASlF"}
|
package/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
export { Bundler } from './src/bundler.js'
|
|
11
|
+
export { DevServer } from './src/dev_server.js'
|
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adonisjs/assembler",
|
|
3
|
-
"version": "6.1.3-
|
|
3
|
+
"version": "6.1.3-4",
|
|
4
4
|
"description": "Provides utilities to run AdonisJS development server and build project for production",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
8
|
+
"src",
|
|
9
|
+
"index.ts",
|
|
8
10
|
"build/src",
|
|
9
11
|
"build/index.d.ts",
|
|
12
|
+
"build/index.d.ts.map",
|
|
10
13
|
"build/index.js"
|
|
11
14
|
],
|
|
12
15
|
"exports": {
|
|
@@ -35,34 +38,34 @@
|
|
|
35
38
|
"author": "virk,adonisjs",
|
|
36
39
|
"license": "MIT",
|
|
37
40
|
"devDependencies": {
|
|
38
|
-
"@commitlint/cli": "^17.
|
|
41
|
+
"@commitlint/cli": "^17.5.0",
|
|
39
42
|
"@commitlint/config-conventional": "^17.4.4",
|
|
40
43
|
"@japa/assert": "^1.4.1",
|
|
41
44
|
"@japa/file-system": "^1.0.1",
|
|
42
45
|
"@japa/run-failed-tests": "^1.1.1",
|
|
43
46
|
"@japa/runner": "^2.5.1",
|
|
44
47
|
"@japa/spec-reporter": "^1.3.3",
|
|
45
|
-
"@swc/core": "^1.3.
|
|
46
|
-
"@types/node": "^18.15.
|
|
48
|
+
"@swc/core": "^1.3.42",
|
|
49
|
+
"@types/node": "^18.15.10",
|
|
47
50
|
"c8": "^7.13.0",
|
|
48
51
|
"cross-env": "^7.0.3",
|
|
49
52
|
"del-cli": "^5.0.0",
|
|
50
53
|
"eslint": "^8.36.0",
|
|
51
|
-
"eslint-config-prettier": "^8.
|
|
54
|
+
"eslint-config-prettier": "^8.8.0",
|
|
52
55
|
"eslint-plugin-adonis": "^3.0.3",
|
|
53
56
|
"eslint-plugin-prettier": "^4.2.1",
|
|
54
57
|
"github-label-sync": "^2.3.1",
|
|
55
58
|
"husky": "^8.0.3",
|
|
56
|
-
"np": "^7.6.
|
|
59
|
+
"np": "^7.6.4",
|
|
57
60
|
"p-event": "^5.0.1",
|
|
58
|
-
"prettier": "^2.8.
|
|
61
|
+
"prettier": "^2.8.7",
|
|
59
62
|
"ts-node": "^10.9.1",
|
|
60
|
-
"typescript": "^
|
|
63
|
+
"typescript": "^5.0.2"
|
|
61
64
|
},
|
|
62
65
|
"dependencies": {
|
|
63
|
-
"@adonisjs/env": "^4.2.0-
|
|
64
|
-
"@poppinss/chokidar-ts": "^4.1.0-
|
|
65
|
-
"@poppinss/cliui": "^6.1.1-
|
|
66
|
+
"@adonisjs/env": "^4.2.0-2",
|
|
67
|
+
"@poppinss/chokidar-ts": "^4.1.0-2",
|
|
68
|
+
"@poppinss/cliui": "^6.1.1-2",
|
|
66
69
|
"@types/picomatch": "^2.3.0",
|
|
67
70
|
"cpy": "^9.0.1",
|
|
68
71
|
"execa": "^7.0.0",
|
package/src/bundler.ts
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
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 slash from 'slash'
|
|
11
|
+
import copyfiles from 'cpy'
|
|
12
|
+
import fs from 'node:fs/promises'
|
|
13
|
+
import type tsStatic from 'typescript'
|
|
14
|
+
import { fileURLToPath } from 'node:url'
|
|
15
|
+
import { join, relative } from 'node:path'
|
|
16
|
+
import { cliui, type Logger } from '@poppinss/cliui'
|
|
17
|
+
|
|
18
|
+
import { run } from './run.js'
|
|
19
|
+
import { parseConfig } from './parse_config.js'
|
|
20
|
+
import type { BundlerOptions } from './types.js'
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Instance of CLIUI
|
|
24
|
+
*/
|
|
25
|
+
const ui = cliui()
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The bundler class exposes the API to build an AdonisJS project.
|
|
29
|
+
*/
|
|
30
|
+
export class Bundler {
|
|
31
|
+
#cwd: URL
|
|
32
|
+
#cwdPath: string
|
|
33
|
+
#ts: typeof tsStatic
|
|
34
|
+
#logger = ui.logger
|
|
35
|
+
#options: BundlerOptions
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Getting reference to colors library from logger
|
|
39
|
+
*/
|
|
40
|
+
get #colors() {
|
|
41
|
+
return this.#logger.getColors()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
constructor(cwd: URL, ts: typeof tsStatic, options: BundlerOptions) {
|
|
45
|
+
this.#cwd = cwd
|
|
46
|
+
this.#cwdPath = fileURLToPath(this.#cwd)
|
|
47
|
+
this.#ts = ts
|
|
48
|
+
this.#options = options
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#getRelativeName(filePath: string) {
|
|
52
|
+
return slash(relative(this.#cwdPath, filePath))
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Cleans up the build directory
|
|
57
|
+
*/
|
|
58
|
+
async #cleanupBuildDirectory(outDir: string) {
|
|
59
|
+
await fs.rm(outDir, { recursive: true, force: true, maxRetries: 5 })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Runs assets bundler command to build assets.
|
|
64
|
+
*/
|
|
65
|
+
async #buildAssets(): Promise<boolean> {
|
|
66
|
+
const assetsBundler = this.#options.assets
|
|
67
|
+
if (!assetsBundler?.serve) {
|
|
68
|
+
return true
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
this.#logger.info('compiling frontend assets', { suffix: assetsBundler.cmd })
|
|
73
|
+
await run(this.#cwd, {
|
|
74
|
+
stdio: 'inherit',
|
|
75
|
+
script: assetsBundler.cmd,
|
|
76
|
+
scriptArgs: [],
|
|
77
|
+
})
|
|
78
|
+
return true
|
|
79
|
+
} catch {
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Runs tsc command to build the source.
|
|
86
|
+
*/
|
|
87
|
+
async #runTsc(outDir: string): Promise<boolean> {
|
|
88
|
+
try {
|
|
89
|
+
await run(this.#cwd, {
|
|
90
|
+
stdio: 'inherit',
|
|
91
|
+
script: 'tsc',
|
|
92
|
+
scriptArgs: ['--outDir', outDir],
|
|
93
|
+
})
|
|
94
|
+
return true
|
|
95
|
+
} catch {
|
|
96
|
+
return false
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Copy files to destination directory
|
|
102
|
+
*/
|
|
103
|
+
async #copyFiles(files: string[], outDir: string) {
|
|
104
|
+
try {
|
|
105
|
+
await copyfiles(files, outDir, { cwd: this.#cwdPath })
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (!error.message.includes("the file doesn't exist")) {
|
|
108
|
+
throw error
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Copy meta files to the output directory
|
|
115
|
+
*/
|
|
116
|
+
async #copyMetaFiles(outDir: string, additionalFilesToCopy: string[]) {
|
|
117
|
+
const metaFiles = (this.#options.metaFiles || [])
|
|
118
|
+
.map((file) => file.pattern)
|
|
119
|
+
.concat(additionalFilesToCopy)
|
|
120
|
+
|
|
121
|
+
await this.#copyFiles(metaFiles, outDir)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Copies .adonisrc.json file to the destination
|
|
126
|
+
*/
|
|
127
|
+
async #copyAdonisRcFile(outDir: string) {
|
|
128
|
+
const existingContents = JSON.parse(
|
|
129
|
+
await fs.readFile(join(this.#cwdPath, '.adonisrc.json'), 'utf-8')
|
|
130
|
+
)
|
|
131
|
+
const compiledContents = Object.assign({}, existingContents, {
|
|
132
|
+
typescript: false,
|
|
133
|
+
lastCompiledAt: new Date().toISOString(),
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
await fs.mkdir(outDir, { recursive: true })
|
|
137
|
+
await fs.writeFile(
|
|
138
|
+
join(outDir, '.adonisrc.json'),
|
|
139
|
+
JSON.stringify(compiledContents, null, 2) + '\n'
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Returns the lock file name for a given packages client
|
|
145
|
+
*/
|
|
146
|
+
#getClientLockFile(client: 'npm' | 'yarn' | 'pnpm') {
|
|
147
|
+
switch (client) {
|
|
148
|
+
case 'npm':
|
|
149
|
+
return 'package-lock.json'
|
|
150
|
+
case 'yarn':
|
|
151
|
+
return 'yarn.lock'
|
|
152
|
+
case 'pnpm':
|
|
153
|
+
return 'pnpm-lock.yaml'
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Returns the installation command for a given packages client
|
|
159
|
+
*/
|
|
160
|
+
#getClientInstallCommand(client: 'npm' | 'yarn' | 'pnpm') {
|
|
161
|
+
switch (client) {
|
|
162
|
+
case 'npm':
|
|
163
|
+
return 'npm ci --omit="dev"'
|
|
164
|
+
case 'yarn':
|
|
165
|
+
return 'yarn install --production'
|
|
166
|
+
case 'pnpm':
|
|
167
|
+
return 'pnpm i --prod'
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Set a custom CLI UI logger
|
|
173
|
+
*/
|
|
174
|
+
setLogger(logger: Logger) {
|
|
175
|
+
this.#logger = logger
|
|
176
|
+
return this
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Bundles the application to be run in production
|
|
181
|
+
*/
|
|
182
|
+
async bundle(
|
|
183
|
+
stopOnError: boolean = true,
|
|
184
|
+
client: 'npm' | 'yarn' | 'pnpm' = 'npm'
|
|
185
|
+
): Promise<boolean> {
|
|
186
|
+
/**
|
|
187
|
+
* Step 1: Parse config file to get the build output directory
|
|
188
|
+
*/
|
|
189
|
+
const config = parseConfig(this.#cwd, this.#ts)
|
|
190
|
+
if (!config) {
|
|
191
|
+
return false
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Step 2: Cleanup existing build directory (if any)
|
|
196
|
+
*/
|
|
197
|
+
const outDir = config.options.outDir || fileURLToPath(new URL('build/', this.#cwd))
|
|
198
|
+
this.#logger.info('cleaning up output directory', { suffix: this.#getRelativeName(outDir) })
|
|
199
|
+
await this.#cleanupBuildDirectory(outDir)
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Step 3: Build frontend assets
|
|
203
|
+
*/
|
|
204
|
+
if (!(await this.#buildAssets())) {
|
|
205
|
+
return false
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Step 4: Build typescript source code
|
|
210
|
+
*/
|
|
211
|
+
this.#logger.info('compiling typescript source', { suffix: 'tsc' })
|
|
212
|
+
const buildCompleted = await this.#runTsc(outDir)
|
|
213
|
+
await this.#copyFiles(['ace.js'], outDir)
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Remove incomplete build directory when tsc build
|
|
217
|
+
* failed and stopOnError is set to true.
|
|
218
|
+
*/
|
|
219
|
+
if (!buildCompleted && stopOnError) {
|
|
220
|
+
await this.#cleanupBuildDirectory(outDir)
|
|
221
|
+
const instructions = ui
|
|
222
|
+
.sticker()
|
|
223
|
+
.fullScreen()
|
|
224
|
+
.drawBorder((borderChar, colors) => colors.red(borderChar))
|
|
225
|
+
|
|
226
|
+
instructions.add(
|
|
227
|
+
this.#colors.red('Cannot complete the build process as there are TypeScript errors.')
|
|
228
|
+
)
|
|
229
|
+
instructions.add(
|
|
230
|
+
this.#colors.red(
|
|
231
|
+
'Use "--ignore-ts-errors" flag to ignore TypeScript errors and continue the build.'
|
|
232
|
+
)
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
this.#logger.logError(instructions.prepare())
|
|
236
|
+
return false
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Step 5: Copy meta files to the build directory
|
|
241
|
+
*/
|
|
242
|
+
const pkgFiles = ['package.json', this.#getClientLockFile(client)]
|
|
243
|
+
this.#logger.info('copying meta files to the output directory')
|
|
244
|
+
await this.#copyMetaFiles(outDir, pkgFiles)
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Step 6: Copy .adonisrc.json file to the build directory
|
|
248
|
+
*/
|
|
249
|
+
this.#logger.info('copying .adonisrc.json file to the output directory')
|
|
250
|
+
await this.#copyAdonisRcFile(outDir)
|
|
251
|
+
|
|
252
|
+
this.#logger.success('build completed')
|
|
253
|
+
this.#logger.log('')
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Next steps
|
|
257
|
+
*/
|
|
258
|
+
ui.instructions()
|
|
259
|
+
.useRenderer(this.#logger.getRenderer())
|
|
260
|
+
.heading('Run the following commands to start the server in production')
|
|
261
|
+
.add(this.#colors.cyan(`cd ${this.#getRelativeName(outDir)}`))
|
|
262
|
+
.add(this.#colors.cyan(this.#getClientInstallCommand(client)))
|
|
263
|
+
.add(this.#colors.cyan('node bin/server.js'))
|
|
264
|
+
.render()
|
|
265
|
+
|
|
266
|
+
return true
|
|
267
|
+
}
|
|
268
|
+
}
|
|
@@ -0,0 +1,470 @@
|
|
|
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 getPort from 'get-port'
|
|
11
|
+
import picomatch from 'picomatch'
|
|
12
|
+
import type tsStatic from 'typescript'
|
|
13
|
+
import { type ExecaChildProcess } from 'execa'
|
|
14
|
+
import type { Watcher } from '@poppinss/chokidar-ts'
|
|
15
|
+
import { cliui, type Logger } from '@poppinss/cliui'
|
|
16
|
+
import { EnvLoader, EnvParser } from '@adonisjs/env'
|
|
17
|
+
|
|
18
|
+
import { watch } from './watch.js'
|
|
19
|
+
import { run, runNode } from './run.js'
|
|
20
|
+
import type { DevServerOptions } from './types.js'
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Instance of CLIUI
|
|
24
|
+
*/
|
|
25
|
+
const ui = cliui()
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Exposes the API to start the development. Optionally, the watch API can be
|
|
29
|
+
* used to watch for file changes and restart the development server.
|
|
30
|
+
*
|
|
31
|
+
* The Dev server performs the following actions
|
|
32
|
+
*
|
|
33
|
+
* - Assigns a random PORT, when PORT inside .env file is in use
|
|
34
|
+
* - Uses tsconfig.json file to collect a list of files to watch.
|
|
35
|
+
* - Uses metaFiles from .adonisrc.json file to collect a list of files to watch.
|
|
36
|
+
* - Restart HTTP server on every file change.
|
|
37
|
+
*/
|
|
38
|
+
export class DevServer {
|
|
39
|
+
#cwd: URL
|
|
40
|
+
#logger = ui.logger
|
|
41
|
+
#options: DevServerOptions
|
|
42
|
+
#isWatching: boolean = false
|
|
43
|
+
#scriptFile: string = 'bin/server.js'
|
|
44
|
+
#httpServerProcess?: ExecaChildProcess<string>
|
|
45
|
+
#isMetaFileWithReloadsEnabled: picomatch.Matcher
|
|
46
|
+
#isMetaFileWithReloadsDisabled: picomatch.Matcher
|
|
47
|
+
#watcher?: ReturnType<Watcher['watch']>
|
|
48
|
+
#assetsServerProcess?: ExecaChildProcess<string>
|
|
49
|
+
#onError?: (error: any) => any
|
|
50
|
+
#onClose?: (exitCode: number) => any
|
|
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
|
+
* Check if file is an .env file
|
|
78
|
+
*/
|
|
79
|
+
#isDotEnvFile(filePath: string) {
|
|
80
|
+
if (filePath === '.env') {
|
|
81
|
+
return true
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return filePath.includes('.env.')
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Check if file is .adonisrc.json file
|
|
89
|
+
*/
|
|
90
|
+
#isRcFile(filePath: string) {
|
|
91
|
+
return filePath === '.adonisrc.json'
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Inspect if child process message is from AdonisJS HTTP server
|
|
96
|
+
*/
|
|
97
|
+
#isAdonisJSReadyMessage(
|
|
98
|
+
message: unknown
|
|
99
|
+
): message is { isAdonisJS: true; environment: 'web'; port: number; host: string } {
|
|
100
|
+
return (
|
|
101
|
+
message !== null &&
|
|
102
|
+
typeof message === 'object' &&
|
|
103
|
+
'isAdonisJS' in message &&
|
|
104
|
+
'environment' in message &&
|
|
105
|
+
message.environment === 'web'
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Conditionally clear the terminal screen
|
|
111
|
+
*/
|
|
112
|
+
#clearScreen() {
|
|
113
|
+
if (this.#options.clearScreen) {
|
|
114
|
+
process.stdout.write('\u001Bc')
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Logs messages from vite dev server stdout and stderr
|
|
120
|
+
*/
|
|
121
|
+
#logViteDevServerMessage(data: Buffer) {
|
|
122
|
+
const dataString = data.toString()
|
|
123
|
+
const lines = dataString.split('\n')
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Logging VITE ready in message with proper
|
|
127
|
+
* spaces and newlines
|
|
128
|
+
*/
|
|
129
|
+
if (dataString.includes('ready in')) {
|
|
130
|
+
console.log('')
|
|
131
|
+
console.log(dataString.trim())
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Put a wrapper around vite network address log
|
|
137
|
+
*/
|
|
138
|
+
if (dataString.includes('Local') && dataString.includes('Network')) {
|
|
139
|
+
const sticker = ui.sticker().useColors(this.#colors).useRenderer(this.#logger.getRenderer())
|
|
140
|
+
|
|
141
|
+
lines.forEach((line: string) => {
|
|
142
|
+
if (line.trim()) {
|
|
143
|
+
sticker.add(line)
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
sticker.render()
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Log rest of the lines
|
|
153
|
+
*/
|
|
154
|
+
lines.forEach((line: string) => {
|
|
155
|
+
if (line.trim()) {
|
|
156
|
+
console.log(line)
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Logs messages from assets dev server stdout and stderr
|
|
163
|
+
*/
|
|
164
|
+
#logAssetsDevServerMessage(data: Buffer) {
|
|
165
|
+
const dataString = data.toString()
|
|
166
|
+
const lines = dataString.split('\n')
|
|
167
|
+
lines.forEach((line: string) => {
|
|
168
|
+
if (line.trim()) {
|
|
169
|
+
console.log(line)
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Returns PORT for starting the HTTP server with option to use
|
|
176
|
+
* a random PORT if main PORT is in use.
|
|
177
|
+
*/
|
|
178
|
+
async #getPort(): Promise<number> {
|
|
179
|
+
/**
|
|
180
|
+
* Use existing port if exists
|
|
181
|
+
*/
|
|
182
|
+
if (process.env.PORT) {
|
|
183
|
+
return getPort({ port: Number(process.env.PORT) })
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const files = await new EnvLoader(this.#cwd).load()
|
|
187
|
+
for (let file of files) {
|
|
188
|
+
const envVariables = new EnvParser(file.contents).parse()
|
|
189
|
+
if (envVariables.PORT) {
|
|
190
|
+
return getPort({ port: Number(envVariables.PORT) })
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return getPort({ port: 3333 })
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Starts the HTTP server
|
|
199
|
+
*/
|
|
200
|
+
#startHTTPServer(port: string, mode: 'blocking' | 'nonblocking') {
|
|
201
|
+
this.#httpServerProcess = runNode(this.#cwd, {
|
|
202
|
+
script: this.#scriptFile,
|
|
203
|
+
env: { PORT: port, ...this.#options.env },
|
|
204
|
+
nodeArgs: this.#options.nodeArgs,
|
|
205
|
+
scriptArgs: this.#options.scriptArgs,
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
this.#httpServerProcess.on('message', (message) => {
|
|
209
|
+
if (this.#isAdonisJSReadyMessage(message)) {
|
|
210
|
+
ui.sticker()
|
|
211
|
+
.useColors(this.#colors)
|
|
212
|
+
.useRenderer(this.#logger.getRenderer())
|
|
213
|
+
.add(`Server address: ${this.#colors.cyan(`http://${message.host}:${message.port}`)}`)
|
|
214
|
+
.add(
|
|
215
|
+
`File system watcher: ${this.#colors.cyan(
|
|
216
|
+
`${this.#isWatching ? 'enabled' : 'disabled'}`
|
|
217
|
+
)}`
|
|
218
|
+
)
|
|
219
|
+
.render()
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
this.#httpServerProcess
|
|
224
|
+
.then((result) => {
|
|
225
|
+
this.#logger.warning(`underlying HTTP server closed with status code "${result.exitCode}"`)
|
|
226
|
+
if (mode === 'nonblocking') {
|
|
227
|
+
this.#onClose?.(result.exitCode)
|
|
228
|
+
this.#watcher?.close()
|
|
229
|
+
}
|
|
230
|
+
})
|
|
231
|
+
.catch((error) => {
|
|
232
|
+
this.#logger.warning('unable to connect to underlying HTTP server process')
|
|
233
|
+
this.#logger.fatal(error)
|
|
234
|
+
this.#onError?.(error)
|
|
235
|
+
this.#watcher?.close()
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Starts the assets bundler server. The assets bundler server process is
|
|
241
|
+
* considered as the secondary process and therefore we do not perform
|
|
242
|
+
* any cleanup if it dies.
|
|
243
|
+
*/
|
|
244
|
+
#startAssetsServer() {
|
|
245
|
+
const assetsBundler = this.#options.assets
|
|
246
|
+
if (!assetsBundler?.serve) {
|
|
247
|
+
return
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
this.#logger.info(`starting "${assetsBundler.driver}" dev server...`)
|
|
251
|
+
this.#assetsServerProcess = run(this.#cwd, {
|
|
252
|
+
script: assetsBundler.cmd,
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* We do not inherit the stdio for vite and encore, because they then
|
|
256
|
+
* own the stdin and interrupts the `Ctrl + C`.
|
|
257
|
+
*/
|
|
258
|
+
stdio: 'pipe',
|
|
259
|
+
scriptArgs: this.#options.scriptArgs,
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Log child process messages
|
|
264
|
+
*/
|
|
265
|
+
this.#assetsServerProcess.stdout?.on('data', (data) => {
|
|
266
|
+
if (assetsBundler.driver === 'vite') {
|
|
267
|
+
this.#logViteDevServerMessage(data)
|
|
268
|
+
} else {
|
|
269
|
+
this.#logAssetsDevServerMessage(data)
|
|
270
|
+
}
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
this.#assetsServerProcess.stderr?.on('data', (data) => {
|
|
274
|
+
if (assetsBundler.driver === 'vite') {
|
|
275
|
+
this.#logViteDevServerMessage(data)
|
|
276
|
+
} else {
|
|
277
|
+
this.#logAssetsDevServerMessage(data)
|
|
278
|
+
}
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
this.#assetsServerProcess
|
|
282
|
+
.then((result) => {
|
|
283
|
+
this.#logger.warning(
|
|
284
|
+
`"${assetsBundler.driver}" dev server closed with status code "${result.exitCode}"`
|
|
285
|
+
)
|
|
286
|
+
})
|
|
287
|
+
.catch((error) => {
|
|
288
|
+
this.#logger.warning(`unable to connect to "${assetsBundler.driver}" dev server`)
|
|
289
|
+
this.#logger.fatal(error)
|
|
290
|
+
})
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Restart the development server
|
|
295
|
+
*/
|
|
296
|
+
#restart(port: string) {
|
|
297
|
+
if (this.#httpServerProcess) {
|
|
298
|
+
this.#httpServerProcess.removeAllListeners()
|
|
299
|
+
this.#httpServerProcess.kill('SIGKILL')
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
this.#startHTTPServer(port, 'blocking')
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Set a custom CLI UI logger
|
|
307
|
+
*/
|
|
308
|
+
setLogger(logger: Logger) {
|
|
309
|
+
this.#logger = logger
|
|
310
|
+
return this
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Add listener to get notified when dev server is
|
|
315
|
+
* closed
|
|
316
|
+
*/
|
|
317
|
+
onClose(callback: (exitCode: number) => any): this {
|
|
318
|
+
this.#onClose = callback
|
|
319
|
+
return this
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Add listener to get notified when dev server exists
|
|
324
|
+
* with an error
|
|
325
|
+
*/
|
|
326
|
+
onError(callback: (error: any) => any): this {
|
|
327
|
+
this.#onError = callback
|
|
328
|
+
return this
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Start the development server
|
|
333
|
+
*/
|
|
334
|
+
async start() {
|
|
335
|
+
this.#clearScreen()
|
|
336
|
+
this.#logger.info('starting HTTP server...')
|
|
337
|
+
this.#startHTTPServer(String(await this.#getPort()), 'nonblocking')
|
|
338
|
+
|
|
339
|
+
this.#startAssetsServer()
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Start the development server in watch mode
|
|
344
|
+
*/
|
|
345
|
+
async startAndWatch(ts: typeof tsStatic, options?: { poll: boolean }) {
|
|
346
|
+
const port = String(await this.#getPort())
|
|
347
|
+
this.#isWatching = true
|
|
348
|
+
|
|
349
|
+
this.#clearScreen()
|
|
350
|
+
this.#logger.info('starting HTTP server...')
|
|
351
|
+
|
|
352
|
+
this.#startHTTPServer(port, 'blocking')
|
|
353
|
+
this.#startAssetsServer()
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Create watcher using tsconfig.json file
|
|
357
|
+
*/
|
|
358
|
+
const output = watch(this.#cwd, ts, options || {})
|
|
359
|
+
if (!output) {
|
|
360
|
+
this.#onClose?.(1)
|
|
361
|
+
return
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Storing reference to watcher, so that we can close it
|
|
366
|
+
* when HTTP server exists with error
|
|
367
|
+
*/
|
|
368
|
+
this.#watcher = output.chokidar
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Notify the watcher is ready
|
|
372
|
+
*/
|
|
373
|
+
output.watcher.on('watcher:ready', () => {
|
|
374
|
+
this.#logger.info('watching file system for changes...')
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Cleanup when watcher dies
|
|
379
|
+
*/
|
|
380
|
+
output.chokidar.on('error', (error) => {
|
|
381
|
+
this.#logger.warning('file system watcher failure')
|
|
382
|
+
this.#logger.fatal(error)
|
|
383
|
+
this.#onError?.(error)
|
|
384
|
+
output.chokidar.close()
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Changes in TypeScript source file
|
|
389
|
+
*/
|
|
390
|
+
output.watcher.on('source:add', ({ relativePath }) => {
|
|
391
|
+
this.#clearScreen()
|
|
392
|
+
this.#logger.log(`${this.#colors.green('add')} ${relativePath}`)
|
|
393
|
+
this.#restart(port)
|
|
394
|
+
})
|
|
395
|
+
output.watcher.on('source:change', ({ relativePath }) => {
|
|
396
|
+
this.#clearScreen()
|
|
397
|
+
this.#logger.log(`${this.#colors.green('update')} ${relativePath}`)
|
|
398
|
+
this.#restart(port)
|
|
399
|
+
})
|
|
400
|
+
output.watcher.on('source:unlink', ({ relativePath }) => {
|
|
401
|
+
this.#clearScreen()
|
|
402
|
+
this.#logger.log(`${this.#colors.green('delete')} ${relativePath}`)
|
|
403
|
+
this.#restart(port)
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Changes in other files
|
|
408
|
+
*/
|
|
409
|
+
output.watcher.on('add', ({ relativePath }) => {
|
|
410
|
+
if (this.#isDotEnvFile(relativePath) || this.#isRcFile(relativePath)) {
|
|
411
|
+
this.#clearScreen()
|
|
412
|
+
this.#logger.log(`${this.#colors.green('add')} ${relativePath}`)
|
|
413
|
+
this.#restart(port)
|
|
414
|
+
return
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (this.#isMetaFileWithReloadsEnabled(relativePath)) {
|
|
418
|
+
this.#clearScreen()
|
|
419
|
+
this.#logger.log(`${this.#colors.green('add')} ${relativePath}`)
|
|
420
|
+
this.#restart(port)
|
|
421
|
+
return
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (this.#isMetaFileWithReloadsDisabled(relativePath)) {
|
|
425
|
+
this.#clearScreen()
|
|
426
|
+
this.#logger.log(`${this.#colors.green('add')} ${relativePath}`)
|
|
427
|
+
}
|
|
428
|
+
})
|
|
429
|
+
output.watcher.on('change', ({ relativePath }) => {
|
|
430
|
+
if (this.#isDotEnvFile(relativePath) || this.#isRcFile(relativePath)) {
|
|
431
|
+
this.#clearScreen()
|
|
432
|
+
this.#logger.log(`${this.#colors.green('update')} ${relativePath}`)
|
|
433
|
+
this.#restart(port)
|
|
434
|
+
return
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (this.#isMetaFileWithReloadsEnabled(relativePath)) {
|
|
438
|
+
this.#clearScreen()
|
|
439
|
+
this.#logger.log(`${this.#colors.green('update')} ${relativePath}`)
|
|
440
|
+
this.#restart(port)
|
|
441
|
+
return
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (this.#isMetaFileWithReloadsDisabled(relativePath)) {
|
|
445
|
+
this.#clearScreen()
|
|
446
|
+
this.#logger.log(`${this.#colors.green('update')} ${relativePath}`)
|
|
447
|
+
}
|
|
448
|
+
})
|
|
449
|
+
output.watcher.on('unlink', ({ relativePath }) => {
|
|
450
|
+
if (this.#isDotEnvFile(relativePath) || this.#isRcFile(relativePath)) {
|
|
451
|
+
this.#clearScreen()
|
|
452
|
+
this.#logger.log(`${this.#colors.green('delete')} ${relativePath}`)
|
|
453
|
+
this.#restart(port)
|
|
454
|
+
return
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (this.#isMetaFileWithReloadsEnabled(relativePath)) {
|
|
458
|
+
this.#clearScreen()
|
|
459
|
+
this.#logger.log(`${this.#colors.green('delete')} ${relativePath}`)
|
|
460
|
+
this.#restart(port)
|
|
461
|
+
return
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (this.#isMetaFileWithReloadsDisabled(relativePath)) {
|
|
465
|
+
this.#clearScreen()
|
|
466
|
+
this.#logger.log(`${this.#colors.green('delete')} ${relativePath}`)
|
|
467
|
+
}
|
|
468
|
+
})
|
|
469
|
+
}
|
|
470
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
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 type tsStatic from 'typescript'
|
|
11
|
+
import { ConfigParser } from '@poppinss/chokidar-ts'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Parses tsconfig.json and prints errors using typescript compiler
|
|
15
|
+
* host
|
|
16
|
+
*/
|
|
17
|
+
export function parseConfig(cwd: string | URL, ts: typeof tsStatic) {
|
|
18
|
+
const { config, error } = new ConfigParser(cwd, 'tsconfig.json', ts).parse()
|
|
19
|
+
if (error) {
|
|
20
|
+
const compilerHost = ts.createCompilerHost({})
|
|
21
|
+
console.log(ts.formatDiagnosticsWithColorAndContext([error], compilerHost))
|
|
22
|
+
return
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (config!.errors.length) {
|
|
26
|
+
const compilerHost = ts.createCompilerHost({})
|
|
27
|
+
console.log(ts.formatDiagnosticsWithColorAndContext(config!.errors, compilerHost))
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return config
|
|
32
|
+
}
|
package/src/run.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
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 { execaNode, execa } from 'execa'
|
|
11
|
+
import type { RunOptions } from './types.js'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Default set of args to pass in order to run TypeScript
|
|
15
|
+
* source
|
|
16
|
+
*/
|
|
17
|
+
const DEFAULT_NODE_ARGS = [
|
|
18
|
+
// Use ts-node/esm loader. The project must install it
|
|
19
|
+
'--loader=ts-node/esm',
|
|
20
|
+
// Disable annonying warnings
|
|
21
|
+
'--no-warnings',
|
|
22
|
+
// Enable expiremental meta resolve for cases where someone uses magic import string
|
|
23
|
+
'--experimental-import-meta-resolve',
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Runs a Node.js script as a child process and inherits the stdio streams
|
|
28
|
+
*/
|
|
29
|
+
export function runNode(cwd: string | URL, options: RunOptions) {
|
|
30
|
+
const childProcess = execaNode(options.script, options.scriptArgs, {
|
|
31
|
+
nodeOptions: DEFAULT_NODE_ARGS.concat(options.nodeArgs),
|
|
32
|
+
preferLocal: true,
|
|
33
|
+
windowsHide: false,
|
|
34
|
+
localDir: cwd,
|
|
35
|
+
cwd,
|
|
36
|
+
buffer: false,
|
|
37
|
+
stdio: options.stdio || 'inherit',
|
|
38
|
+
env: {
|
|
39
|
+
...(options.stdio === 'pipe' ? { FORCE_COLOR: 'true' } : {}),
|
|
40
|
+
...options.env,
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
return childProcess
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Runs a script as a child process and inherits the stdio streams
|
|
49
|
+
*/
|
|
50
|
+
export function run(cwd: string | URL, options: Omit<RunOptions, 'nodeArgs'>) {
|
|
51
|
+
const childProcess = execa(options.script, options.scriptArgs, {
|
|
52
|
+
preferLocal: true,
|
|
53
|
+
windowsHide: false,
|
|
54
|
+
localDir: cwd,
|
|
55
|
+
cwd,
|
|
56
|
+
buffer: false,
|
|
57
|
+
stdio: options.stdio || 'inherit',
|
|
58
|
+
env: {
|
|
59
|
+
...(options.stdio === 'pipe' ? { FORCE_COLOR: 'true' } : {}),
|
|
60
|
+
...options.env,
|
|
61
|
+
},
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
return childProcess
|
|
65
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
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
|
+
/**
|
|
11
|
+
* Options needed to run a script file
|
|
12
|
+
*/
|
|
13
|
+
export type RunOptions = {
|
|
14
|
+
script: string
|
|
15
|
+
scriptArgs: string[]
|
|
16
|
+
nodeArgs: string[]
|
|
17
|
+
stdio?: 'pipe' | 'inherit'
|
|
18
|
+
env?: NodeJS.ProcessEnv
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Watcher options
|
|
23
|
+
*/
|
|
24
|
+
export type WatchOptions = {
|
|
25
|
+
poll?: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Meta file config defined in ".adonisrc.json" file
|
|
30
|
+
*/
|
|
31
|
+
export type MetaFile = {
|
|
32
|
+
pattern: string
|
|
33
|
+
reloadServer: boolean
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Options accepted by assets bundler
|
|
38
|
+
*/
|
|
39
|
+
export type AssetsBundlerOptions =
|
|
40
|
+
| {
|
|
41
|
+
serve: false
|
|
42
|
+
driver?: string
|
|
43
|
+
cmd?: string
|
|
44
|
+
}
|
|
45
|
+
| {
|
|
46
|
+
serve: true
|
|
47
|
+
driver: string
|
|
48
|
+
cmd: string
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Options accepted by the dev server
|
|
53
|
+
*/
|
|
54
|
+
export type DevServerOptions = {
|
|
55
|
+
scriptArgs: string[]
|
|
56
|
+
nodeArgs: string[]
|
|
57
|
+
clearScreen?: boolean
|
|
58
|
+
env?: NodeJS.ProcessEnv
|
|
59
|
+
metaFiles?: MetaFile[]
|
|
60
|
+
assets?: AssetsBundlerOptions
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Options accepted by the project bundler
|
|
65
|
+
*/
|
|
66
|
+
export type BundlerOptions = {
|
|
67
|
+
metaFiles?: MetaFile[]
|
|
68
|
+
assets?: AssetsBundlerOptions
|
|
69
|
+
}
|
package/src/watch.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
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 type tsStatic from 'typescript'
|
|
11
|
+
import { fileURLToPath } from 'node:url'
|
|
12
|
+
import { Watcher } from '@poppinss/chokidar-ts'
|
|
13
|
+
|
|
14
|
+
import type { WatchOptions } from './types.js'
|
|
15
|
+
import { parseConfig } from './parse_config.js'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Watches the file system using tsconfig file
|
|
19
|
+
*/
|
|
20
|
+
export function watch(cwd: string | URL, ts: typeof tsStatic, options: WatchOptions) {
|
|
21
|
+
const config = parseConfig(cwd, ts)
|
|
22
|
+
if (!config) {
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const watcher = new Watcher(typeof cwd === 'string' ? cwd : fileURLToPath(cwd), config!)
|
|
27
|
+
const chokidar = watcher.watch(['.'], { usePolling: options.poll })
|
|
28
|
+
return { watcher, chokidar }
|
|
29
|
+
}
|