@adonisjs/assembler 8.0.0-next.5 → 8.0.0-next.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/README.md +87 -59
- package/build/chunk-25Q3N5JR.js +392 -0
- package/build/chunk-PORDZS62.js +391 -0
- package/build/chunk-TIKQQRMX.js +116 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +825 -430
- package/build/src/bundler.d.ts +44 -3
- package/build/src/code_scanners/routes_scanner/main.d.ts +49 -9
- package/build/src/code_scanners/routes_scanner/main.js +445 -0
- package/build/src/code_scanners/routes_scanner/validator_extractor.d.ts +12 -4
- package/build/src/code_transformer/main.d.ts +44 -43
- package/build/src/code_transformer/main.js +123 -101
- package/build/src/code_transformer/rc_file_transformer.d.ts +56 -4
- package/build/src/debug.d.ts +12 -0
- package/build/src/dev_server.d.ts +38 -9
- package/build/src/file_buffer.d.ts +67 -0
- package/build/src/file_system.d.ts +45 -7
- package/build/src/helpers.d.ts +79 -4
- package/build/src/helpers.js +16 -0
- package/build/src/hooks.d.ts +224 -0
- package/build/src/index_generator/main.d.ts +64 -0
- package/build/src/index_generator/main.js +7 -0
- package/build/src/index_generator/source.d.ts +60 -0
- package/build/src/paths_resolver.d.ts +27 -2
- package/build/src/shortcuts_manager.d.ts +42 -4
- package/build/src/test_runner.d.ts +56 -10
- package/build/src/types/code_scanners.d.ts +138 -24
- package/build/src/types/code_transformer.d.ts +61 -19
- package/build/src/types/common.d.ts +199 -55
- package/build/src/types/hooks.d.ts +235 -22
- package/build/src/types/main.d.ts +13 -0
- package/build/src/utils.d.ts +88 -13
- package/build/src/virtual_file_system.d.ts +112 -0
- package/package.json +9 -3
- package/build/chunk-RR4HCA4M.js +0 -7
- package/build/src/ast_file_system.d.ts +0 -17
package/build/index.js
CHANGED
|
@@ -1,183 +1,252 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "./chunk-
|
|
2
|
+
IndexGenerator
|
|
3
|
+
} from "./chunk-25Q3N5JR.js";
|
|
4
|
+
import {
|
|
5
|
+
copyFiles,
|
|
6
|
+
debug_default,
|
|
7
|
+
getPort,
|
|
8
|
+
loadHooks,
|
|
9
|
+
memoize,
|
|
10
|
+
parseConfig,
|
|
11
|
+
run,
|
|
12
|
+
runNode,
|
|
13
|
+
throttle,
|
|
14
|
+
watch
|
|
15
|
+
} from "./chunk-PORDZS62.js";
|
|
16
|
+
|
|
17
|
+
// src/hooks.ts
|
|
18
|
+
var hooks = {
|
|
19
|
+
/**
|
|
20
|
+
* Hook called during application initialization. This is the first hook
|
|
21
|
+
* that gets executed when the assembler starts up.
|
|
22
|
+
*
|
|
23
|
+
* @param callback - Function to execute when the init event occurs
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```js
|
|
27
|
+
* hooks.init((app) => {
|
|
28
|
+
* console.log('Application is initializing')
|
|
29
|
+
* // Setup global configurations
|
|
30
|
+
* })
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
init(callback) {
|
|
34
|
+
return callback;
|
|
35
|
+
},
|
|
36
|
+
/**
|
|
37
|
+
* Hook called after routes have been committed to the router.
|
|
38
|
+
* This occurs after all route definitions have been processed.
|
|
39
|
+
*
|
|
40
|
+
* @param callback - Function to execute when routes are committed
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```js
|
|
44
|
+
* hooks.routesCommitted((router) => {
|
|
45
|
+
* console.log('All routes have been committed to the router')
|
|
46
|
+
* // Perform route-based setup
|
|
47
|
+
* })
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
routesCommitted(callback) {
|
|
51
|
+
return callback;
|
|
52
|
+
},
|
|
53
|
+
/**
|
|
54
|
+
* Hook called when the assembler starts scanning for route files.
|
|
55
|
+
* This happens before any route files are actually processed.
|
|
56
|
+
*
|
|
57
|
+
* @param callback - Function to execute when route scanning begins
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```js
|
|
61
|
+
* hooks.routesScanning(() => {
|
|
62
|
+
* console.log('Starting to scan for route files')
|
|
63
|
+
* // Setup route scanning configurations
|
|
64
|
+
* })
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
routesScanning(callback) {
|
|
68
|
+
return callback;
|
|
69
|
+
},
|
|
70
|
+
/**
|
|
71
|
+
* Hook called after all route files have been scanned and processed.
|
|
72
|
+
* This occurs once the route scanning phase is complete.
|
|
73
|
+
*
|
|
74
|
+
* @param callback - Function to execute when route scanning is finished
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```js
|
|
78
|
+
* hooks.routesScanned((scannedRoutes) => {
|
|
79
|
+
* console.log('Route scanning completed')
|
|
80
|
+
* // Process scanned route information
|
|
81
|
+
* })
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
routesScanned(callback) {
|
|
85
|
+
return callback;
|
|
86
|
+
},
|
|
87
|
+
/**
|
|
88
|
+
* Hook called when a file is modified during development.
|
|
89
|
+
* This is triggered by the file watcher when changes are detected.
|
|
90
|
+
*
|
|
91
|
+
* @param callback - Function to execute when a file changes
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```js
|
|
95
|
+
* hooks.fileChanged((filePath, stats) => {
|
|
96
|
+
* console.log(`File changed: ${filePath}`)
|
|
97
|
+
* // Handle file change logic
|
|
98
|
+
* })
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
fileChanged(callback) {
|
|
102
|
+
return callback;
|
|
103
|
+
},
|
|
104
|
+
/**
|
|
105
|
+
* Hook called when a new file is added during development.
|
|
106
|
+
* This is triggered by the file watcher when new files are created.
|
|
107
|
+
*
|
|
108
|
+
* @param callback - Function to execute when a file is added
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```js
|
|
112
|
+
* hooks.fileAdded((filePath, stats) => {
|
|
113
|
+
* console.log(`New file added: ${filePath}`)
|
|
114
|
+
* // Handle new file logic
|
|
115
|
+
* })
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
fileAdded(callback) {
|
|
119
|
+
return callback;
|
|
120
|
+
},
|
|
121
|
+
/**
|
|
122
|
+
* Hook called when a file is removed during development.
|
|
123
|
+
* This is triggered by the file watcher when files are deleted.
|
|
124
|
+
*
|
|
125
|
+
* @param callback - Function to execute when a file is removed
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```js
|
|
129
|
+
* hooks.fileRemoved((filePath) => {
|
|
130
|
+
* console.log(`File removed: ${filePath}`)
|
|
131
|
+
* // Handle file removal logic
|
|
132
|
+
* })
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
fileRemoved(callback) {
|
|
136
|
+
return callback;
|
|
137
|
+
},
|
|
138
|
+
/**
|
|
139
|
+
* Hook called when the development server is about to start.
|
|
140
|
+
* This occurs before the server begins listening for connections.
|
|
141
|
+
*
|
|
142
|
+
* @param callback - Function to execute when dev server is starting
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```js
|
|
146
|
+
* hooks.devServerStarting((server) => {
|
|
147
|
+
* console.log('Development server is starting')
|
|
148
|
+
* // Setup server configurations
|
|
149
|
+
* })
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
devServerStarting(callback) {
|
|
153
|
+
return callback;
|
|
154
|
+
},
|
|
155
|
+
/**
|
|
156
|
+
* Hook called after the development server has successfully started.
|
|
157
|
+
* This occurs once the server is listening and ready to accept requests.
|
|
158
|
+
*
|
|
159
|
+
* @param callback - Function to execute when dev server has started
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```js
|
|
163
|
+
* hooks.devServerStarted((server) => {
|
|
164
|
+
* console.log(`Development server started on port ${server.port}`)
|
|
165
|
+
* // Notify external services or open browser
|
|
166
|
+
* })
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
devServerStarted(callback) {
|
|
170
|
+
return callback;
|
|
171
|
+
},
|
|
172
|
+
/**
|
|
173
|
+
* Hook called when the build process is about to start.
|
|
174
|
+
* This occurs before any build tasks are executed.
|
|
175
|
+
*
|
|
176
|
+
* @param callback - Function to execute when build is starting
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```js
|
|
180
|
+
* hooks.buildStarting((buildConfig) => {
|
|
181
|
+
* console.log('Build process is starting')
|
|
182
|
+
* // Setup build configurations or clean directories
|
|
183
|
+
* })
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
buildStarting(callback) {
|
|
187
|
+
return callback;
|
|
188
|
+
},
|
|
189
|
+
/**
|
|
190
|
+
* Hook called after the build process has completed.
|
|
191
|
+
* This occurs once all build tasks have finished executing.
|
|
192
|
+
*
|
|
193
|
+
* @param callback - Function to execute when build is finished
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```js
|
|
197
|
+
* hooks.buildFinished((buildResult) => {
|
|
198
|
+
* console.log('Build process completed')
|
|
199
|
+
* // Deploy artifacts or notify build completion
|
|
200
|
+
* })
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
203
|
+
buildFinished(callback) {
|
|
204
|
+
return callback;
|
|
205
|
+
},
|
|
206
|
+
/**
|
|
207
|
+
* Hook called when the test suite is about to start.
|
|
208
|
+
* This occurs before any test files are executed.
|
|
209
|
+
*
|
|
210
|
+
* @param callback - Function to execute when tests are starting
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```js
|
|
214
|
+
* hooks.testsStarting((testConfig) => {
|
|
215
|
+
* console.log('Test suite is starting')
|
|
216
|
+
* // Setup test database or mock services
|
|
217
|
+
* })
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
220
|
+
testsStarting(callback) {
|
|
221
|
+
return callback;
|
|
222
|
+
},
|
|
223
|
+
/**
|
|
224
|
+
* Hook called after the test suite has completed.
|
|
225
|
+
* This occurs once all test files have finished executing.
|
|
226
|
+
*
|
|
227
|
+
* @param callback - Function to execute when tests are finished
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```js
|
|
231
|
+
* hooks.testsFinished((testResults) => {
|
|
232
|
+
* console.log('Test suite completed')
|
|
233
|
+
* // Generate test reports or cleanup test resources
|
|
234
|
+
* })
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
testsFinished(callback) {
|
|
238
|
+
return callback;
|
|
239
|
+
}
|
|
240
|
+
};
|
|
4
241
|
|
|
5
242
|
// src/bundler.ts
|
|
6
243
|
import dedent from "dedent";
|
|
7
244
|
import fs from "fs/promises";
|
|
8
245
|
import { cliui } from "@poppinss/cliui";
|
|
9
|
-
import { fileURLToPath
|
|
10
|
-
import { join as join2, relative as relative2 } from "path";
|
|
246
|
+
import { fileURLToPath } from "url";
|
|
11
247
|
import string from "@poppinss/utils/string";
|
|
248
|
+
import { join, relative } from "path/posix";
|
|
12
249
|
import { detectPackageManager } from "@antfu/install-pkg";
|
|
13
|
-
|
|
14
|
-
// src/utils.ts
|
|
15
|
-
import Cache from "tmp-cache";
|
|
16
|
-
import { isJunk } from "junk";
|
|
17
|
-
import fastGlob from "fast-glob";
|
|
18
|
-
import Hooks from "@poppinss/hooks";
|
|
19
|
-
import { existsSync } from "fs";
|
|
20
|
-
import getRandomPort from "get-port";
|
|
21
|
-
import { fileURLToPath } from "url";
|
|
22
|
-
import { execaNode, execa } from "execa";
|
|
23
|
-
import { importDefault } from "@poppinss/utils";
|
|
24
|
-
import { copyFile, mkdir } from "fs/promises";
|
|
25
|
-
import { EnvLoader, EnvParser } from "@adonisjs/env";
|
|
26
|
-
import chokidar from "chokidar";
|
|
27
|
-
import { basename, dirname, isAbsolute, join, relative } from "path";
|
|
28
|
-
var DEFAULT_NODE_ARGS = ["--import=@poppinss/ts-exec", "--enable-source-maps"];
|
|
29
|
-
function parseConfig(cwd, ts) {
|
|
30
|
-
const cwdPath = typeof cwd === "string" ? cwd : fileURLToPath(cwd);
|
|
31
|
-
const configFile = join(cwdPath, "tsconfig.json");
|
|
32
|
-
debug_default('parsing config file "%s"', configFile);
|
|
33
|
-
let hardException = null;
|
|
34
|
-
const parsedConfig = ts.getParsedCommandLineOfConfigFile(
|
|
35
|
-
configFile,
|
|
36
|
-
{},
|
|
37
|
-
{
|
|
38
|
-
...ts.sys,
|
|
39
|
-
useCaseSensitiveFileNames: true,
|
|
40
|
-
getCurrentDirectory: () => cwdPath,
|
|
41
|
-
onUnRecoverableConfigFileDiagnostic: (error) => hardException = error
|
|
42
|
-
}
|
|
43
|
-
);
|
|
44
|
-
if (hardException) {
|
|
45
|
-
const compilerHost = ts.createCompilerHost({});
|
|
46
|
-
console.log(ts.formatDiagnosticsWithColorAndContext([hardException], compilerHost));
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
if (parsedConfig.errors.length) {
|
|
50
|
-
const compilerHost = ts.createCompilerHost({});
|
|
51
|
-
console.log(ts.formatDiagnosticsWithColorAndContext(parsedConfig.errors, compilerHost));
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
return parsedConfig;
|
|
55
|
-
}
|
|
56
|
-
function runNode(cwd, options) {
|
|
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
|
-
reject: options.reject ?? false,
|
|
64
|
-
buffer: false,
|
|
65
|
-
stdio: options.stdio || "inherit",
|
|
66
|
-
env: {
|
|
67
|
-
...options.stdio === "pipe" ? { FORCE_COLOR: "true" } : {},
|
|
68
|
-
...options.env
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
return childProcess;
|
|
72
|
-
}
|
|
73
|
-
function run(cwd, options) {
|
|
74
|
-
const childProcess = execa(options.script, options.scriptArgs, {
|
|
75
|
-
preferLocal: true,
|
|
76
|
-
windowsHide: false,
|
|
77
|
-
localDir: cwd,
|
|
78
|
-
cwd,
|
|
79
|
-
buffer: false,
|
|
80
|
-
stdio: options.stdio || "inherit",
|
|
81
|
-
env: {
|
|
82
|
-
...options.stdio === "pipe" ? { FORCE_COLOR: "true" } : {},
|
|
83
|
-
...options.env
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
return childProcess;
|
|
87
|
-
}
|
|
88
|
-
function watch(options) {
|
|
89
|
-
return chokidar.watch(["."], options);
|
|
90
|
-
}
|
|
91
|
-
async function getPort(cwd) {
|
|
92
|
-
if (process.env.PORT) {
|
|
93
|
-
return getRandomPort({ port: Number(process.env.PORT) });
|
|
94
|
-
}
|
|
95
|
-
const files = await new EnvLoader(cwd).load();
|
|
96
|
-
for (let file of files) {
|
|
97
|
-
const envVariables = await new EnvParser(file.contents).parse();
|
|
98
|
-
if (envVariables.PORT) {
|
|
99
|
-
return getRandomPort({ port: Number(envVariables.PORT) });
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
return getRandomPort({ port: 3333 });
|
|
103
|
-
}
|
|
104
|
-
async function copyFiles(files, cwd, outDir) {
|
|
105
|
-
const { paths, patterns } = files.reduce(
|
|
106
|
-
(result, file) => {
|
|
107
|
-
if (fastGlob.isDynamicPattern(file)) {
|
|
108
|
-
result.patterns.push(file);
|
|
109
|
-
return result;
|
|
110
|
-
}
|
|
111
|
-
if (existsSync(join(cwd, file))) {
|
|
112
|
-
result.paths.push(file);
|
|
113
|
-
}
|
|
114
|
-
return result;
|
|
115
|
-
},
|
|
116
|
-
{ patterns: [], paths: [] }
|
|
117
|
-
);
|
|
118
|
-
debug_default("copyFiles inputs: %O, paths: %O, patterns: %O", files, paths, patterns);
|
|
119
|
-
const filePaths = paths.concat(await fastGlob(patterns, { cwd, dot: true })).filter((file) => {
|
|
120
|
-
return !isJunk(basename(file));
|
|
121
|
-
});
|
|
122
|
-
debug_default('copying files %O to destination "%s"', filePaths, outDir);
|
|
123
|
-
const copyPromises = filePaths.map(async (file) => {
|
|
124
|
-
const src = isAbsolute(file) ? file : join(cwd, file);
|
|
125
|
-
const dest = join(outDir, relative(cwd, src));
|
|
126
|
-
await mkdir(dirname(dest), { recursive: true });
|
|
127
|
-
return copyFile(src, dest);
|
|
128
|
-
});
|
|
129
|
-
return await Promise.all(copyPromises);
|
|
130
|
-
}
|
|
131
|
-
function memoize(fn, maxKeys) {
|
|
132
|
-
const cache = new Cache({ max: maxKeys });
|
|
133
|
-
return (input) => {
|
|
134
|
-
if (cache.has(input)) {
|
|
135
|
-
return cache.get(input);
|
|
136
|
-
}
|
|
137
|
-
return fn(input);
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
async function loadHooks(rcFileHooks, names) {
|
|
141
|
-
const groups = names.map((name) => {
|
|
142
|
-
return {
|
|
143
|
-
group: name,
|
|
144
|
-
hooks: rcFileHooks?.[name] ?? []
|
|
145
|
-
};
|
|
146
|
-
});
|
|
147
|
-
const hooks = new Hooks();
|
|
148
|
-
for (const { group, hooks: collection } of groups) {
|
|
149
|
-
for (const item of collection) {
|
|
150
|
-
hooks.add(group, await importDefault(item));
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
return hooks;
|
|
154
|
-
}
|
|
155
|
-
function throttle(fn, name) {
|
|
156
|
-
name = name || "throttled";
|
|
157
|
-
let isBusy = false;
|
|
158
|
-
let hasQueuedCalls = false;
|
|
159
|
-
let lastCallArgs;
|
|
160
|
-
async function throttled(...args) {
|
|
161
|
-
if (isBusy) {
|
|
162
|
-
debug_default('ignoring "%s" invocation as current execution is in progress', name);
|
|
163
|
-
hasQueuedCalls = true;
|
|
164
|
-
lastCallArgs = args;
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
isBusy = true;
|
|
168
|
-
debug_default('executing "%s" function', name);
|
|
169
|
-
await fn(...args);
|
|
170
|
-
isBusy = false;
|
|
171
|
-
if (hasQueuedCalls) {
|
|
172
|
-
hasQueuedCalls = false;
|
|
173
|
-
debug_default('resuming and running latest "%s" invocation', name);
|
|
174
|
-
await throttled(...lastCallArgs);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return throttled;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// src/bundler.ts
|
|
181
250
|
var SUPPORTED_PACKAGE_MANAGERS = {
|
|
182
251
|
"npm": {
|
|
183
252
|
packageManagerFiles: ["package-lock.json"],
|
|
@@ -201,25 +270,53 @@ var SUPPORTED_PACKAGE_MANAGERS = {
|
|
|
201
270
|
}
|
|
202
271
|
};
|
|
203
272
|
var Bundler = class {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
this.#cwdPath = fileURLToPath2(this.cwd);
|
|
208
|
-
this.#ts = ts;
|
|
209
|
-
}
|
|
210
|
-
#cwdPath;
|
|
273
|
+
/**
|
|
274
|
+
* Reference to the TypeScript module
|
|
275
|
+
*/
|
|
211
276
|
#ts;
|
|
212
277
|
/**
|
|
213
278
|
* Hooks to execute custom actions during the build process
|
|
214
279
|
*/
|
|
215
280
|
#hooks;
|
|
281
|
+
/**
|
|
282
|
+
* Index generator for managing auto-generated index files
|
|
283
|
+
*/
|
|
284
|
+
#indexGenerator;
|
|
285
|
+
/**
|
|
286
|
+
* CLI UI instance for displaying colorful messages and progress information
|
|
287
|
+
*/
|
|
216
288
|
ui = cliui();
|
|
289
|
+
/**
|
|
290
|
+
* The current working directory URL
|
|
291
|
+
*/
|
|
292
|
+
cwd;
|
|
293
|
+
/**
|
|
294
|
+
* The current working project directory path as string
|
|
295
|
+
*/
|
|
296
|
+
cwdPath;
|
|
297
|
+
/**
|
|
298
|
+
* Bundler configuration options including hooks and meta files
|
|
299
|
+
*/
|
|
300
|
+
options;
|
|
301
|
+
/**
|
|
302
|
+
* Create a new bundler instance
|
|
303
|
+
*
|
|
304
|
+
* @param cwd - The current working directory URL
|
|
305
|
+
* @param ts - TypeScript module reference
|
|
306
|
+
* @param options - Bundler configuration options
|
|
307
|
+
*/
|
|
308
|
+
constructor(cwd, ts, options) {
|
|
309
|
+
this.cwd = cwd;
|
|
310
|
+
this.options = options;
|
|
311
|
+
this.cwdPath = string.toUnixSlash(fileURLToPath(this.cwd));
|
|
312
|
+
this.#ts = ts;
|
|
313
|
+
}
|
|
217
314
|
/**
|
|
218
315
|
* Returns the relative unix path for an absolute
|
|
219
316
|
* file path
|
|
220
317
|
*/
|
|
221
318
|
#getRelativeName(filePath) {
|
|
222
|
-
return string.toUnixSlash(
|
|
319
|
+
return string.toUnixSlash(relative(this.cwdPath, filePath));
|
|
223
320
|
}
|
|
224
321
|
/**
|
|
225
322
|
* Cleans up the build directory
|
|
@@ -247,13 +344,13 @@ var Bundler = class {
|
|
|
247
344
|
*/
|
|
248
345
|
async #copyMetaFiles(outDir, additionalFilesToCopy) {
|
|
249
346
|
const metaFiles = (this.options.metaFiles || []).map((file) => file.pattern).concat(additionalFilesToCopy);
|
|
250
|
-
await copyFiles(metaFiles, this
|
|
347
|
+
await copyFiles(metaFiles, this.cwdPath, outDir);
|
|
251
348
|
}
|
|
252
349
|
/**
|
|
253
350
|
* Detect the package manager used by the project
|
|
254
351
|
*/
|
|
255
352
|
async #detectPackageManager() {
|
|
256
|
-
const pkgManager = await detectPackageManager(this
|
|
353
|
+
const pkgManager = await detectPackageManager(this.cwdPath);
|
|
257
354
|
if (pkgManager === "deno") {
|
|
258
355
|
return "npm";
|
|
259
356
|
}
|
|
@@ -268,7 +365,7 @@ var Bundler = class {
|
|
|
268
365
|
* in a production environment.
|
|
269
366
|
*/
|
|
270
367
|
async #createAceFile(outDir) {
|
|
271
|
-
const aceFileLocation =
|
|
368
|
+
const aceFileLocation = join(outDir, "ace.js");
|
|
272
369
|
const aceFileContent = dedent(
|
|
273
370
|
/* JavaScript */
|
|
274
371
|
`
|
|
@@ -286,15 +383,28 @@ var Bundler = class {
|
|
|
286
383
|
}
|
|
287
384
|
/**
|
|
288
385
|
* Bundles the application to be run in production
|
|
386
|
+
*
|
|
387
|
+
* @param stopOnError - Whether to stop the build process on TypeScript errors
|
|
388
|
+
* @param client - Override the detected package manager
|
|
389
|
+
* @returns Promise that resolves to true if build succeeded, false otherwise
|
|
390
|
+
*
|
|
391
|
+
* @example
|
|
392
|
+
* const success = await bundler.bundle(true, 'npm')
|
|
289
393
|
*/
|
|
290
394
|
async bundle(stopOnError = true, client) {
|
|
291
|
-
this.#hooks = await loadHooks(this.options.hooks, ["buildStarting", "buildFinished"]);
|
|
292
395
|
this.packageManager = client ?? await this.#detectPackageManager() ?? "npm";
|
|
293
396
|
const config = parseConfig(this.cwd, this.#ts);
|
|
294
397
|
if (!config) {
|
|
295
398
|
return false;
|
|
296
399
|
}
|
|
297
|
-
|
|
400
|
+
this.ui.logger.info("loading hooks...");
|
|
401
|
+
this.#hooks = await loadHooks(this.options.hooks, ["init", "buildStarting", "buildFinished"]);
|
|
402
|
+
this.#indexGenerator = new IndexGenerator(this.cwdPath, this.ui.logger);
|
|
403
|
+
await this.#hooks.runner("init").run(this, this.#indexGenerator);
|
|
404
|
+
this.#hooks.clear("init");
|
|
405
|
+
this.ui.logger.info("generating indexes...");
|
|
406
|
+
await this.#indexGenerator.generate();
|
|
407
|
+
const outDir = config.options.outDir || fileURLToPath(new URL("build/", this.cwd));
|
|
298
408
|
this.ui.logger.info("cleaning up output directory", { suffix: this.#getRelativeName(outDir) });
|
|
299
409
|
await this.#cleanupBuildDirectory(outDir);
|
|
300
410
|
await this.#hooks.runner("buildStarting").run(this);
|
|
@@ -331,20 +441,19 @@ var Bundler = class {
|
|
|
331
441
|
};
|
|
332
442
|
|
|
333
443
|
// src/dev_server.ts
|
|
334
|
-
import { relative as relative4 } from "path";
|
|
335
444
|
import { cliui as cliui2 } from "@poppinss/cliui";
|
|
336
445
|
import prettyHrtime from "pretty-hrtime";
|
|
337
|
-
import { fileURLToPath as
|
|
446
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
338
447
|
import string3 from "@poppinss/utils/string";
|
|
448
|
+
import { join as join2, relative as relative3 } from "path/posix";
|
|
339
449
|
import { RuntimeException } from "@poppinss/utils/exception";
|
|
340
450
|
|
|
341
451
|
// src/file_system.ts
|
|
342
452
|
import picomatch from "picomatch";
|
|
343
|
-
import {
|
|
344
|
-
import { join as join3, relative as relative3 } from "path";
|
|
453
|
+
import { relative as relative2 } from "path/posix";
|
|
345
454
|
import string2 from "@poppinss/utils/string";
|
|
346
455
|
var DEFAULT_INCLUDES = ["**/*"];
|
|
347
|
-
var ALWAYS_EXCLUDE = [".git/**", "coverage/**", ".github/**"];
|
|
456
|
+
var ALWAYS_EXCLUDE = [".git/**", "coverage/**", ".github/**", ".adonisjs/**"];
|
|
348
457
|
var DEFAULT_EXCLUDES = ["node_modules/**", "bower_components/**", "jspm_packages/**"];
|
|
349
458
|
var FileSystem = class {
|
|
350
459
|
/**
|
|
@@ -386,7 +495,7 @@ var FileSystem = class {
|
|
|
386
495
|
*/
|
|
387
496
|
#isTestFile;
|
|
388
497
|
/**
|
|
389
|
-
* References to includes and excludes
|
|
498
|
+
* References to includes and excludes glob patterns
|
|
390
499
|
*/
|
|
391
500
|
#includes;
|
|
392
501
|
#excludes;
|
|
@@ -414,11 +523,25 @@ var FileSystem = class {
|
|
|
414
523
|
return this.#excludes;
|
|
415
524
|
}
|
|
416
525
|
/**
|
|
417
|
-
* Inspect a
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
526
|
+
* Inspect a file path to determine its type and properties within the project.
|
|
527
|
+
*
|
|
528
|
+
* This method analyzes a file to categorize it as a script file, test file, or meta file,
|
|
529
|
+
* and determines whether changes to the file should trigger server restarts. Results
|
|
530
|
+
* are memoized for performance optimization.
|
|
531
|
+
*
|
|
532
|
+
* @param absolutePath - The absolute Unix path to the file
|
|
533
|
+
* @param relativePath - The relative Unix path from the project root
|
|
534
|
+
* @returns File inspection result or null if the file should be ignored
|
|
535
|
+
*
|
|
536
|
+
* @example
|
|
537
|
+
* const file = fileSystem.inspect('/project/app/models/user.ts', 'app/models/user.ts')
|
|
538
|
+
* if (file) {
|
|
539
|
+
* console.log(file.fileType) // 'script'
|
|
540
|
+
* console.log(file.reloadServer) // true
|
|
541
|
+
* }
|
|
542
|
+
*/
|
|
543
|
+
inspect = memoize((absolutePath, relativePath) => {
|
|
544
|
+
relativePath = relativePath ?? relative2(this.#cwd, absolutePath);
|
|
422
545
|
if (this.#isScriptFile(relativePath) && (this.#scannedTypeScriptFiles.has(absolutePath) || this.#isPartOfBackendProject(relativePath))) {
|
|
423
546
|
debug_default('backend project file "%s"', relativePath);
|
|
424
547
|
const isTestFile = this.#isTestFile(relativePath);
|
|
@@ -451,63 +574,73 @@ var FileSystem = class {
|
|
|
451
574
|
return null;
|
|
452
575
|
});
|
|
453
576
|
/**
|
|
454
|
-
*
|
|
455
|
-
*
|
|
577
|
+
* Determines if a directory should be watched by the file watcher.
|
|
578
|
+
*
|
|
579
|
+
* This method checks if a directory should be monitored for file changes
|
|
580
|
+
* based on the TypeScript configuration includes/excludes patterns.
|
|
581
|
+
* Results are memoized for performance. Chokidar sends absolute Unix paths.
|
|
456
582
|
*
|
|
457
|
-
*
|
|
458
|
-
*
|
|
583
|
+
* Note: Use shouldWatchFile for files and this method for directories only.
|
|
584
|
+
*
|
|
585
|
+
* @param absolutePath - The absolute Unix path to the directory
|
|
586
|
+
* @returns True if the directory should be watched
|
|
587
|
+
*
|
|
588
|
+
* @example
|
|
589
|
+
* const shouldWatch = fileSystem.shouldWatchDirectory('/project/app/controllers')
|
|
590
|
+
* console.log(shouldWatch) // true
|
|
459
591
|
*/
|
|
460
592
|
shouldWatchDirectory = memoize((absolutePath) => {
|
|
461
593
|
if (absolutePath === this.#cwd) {
|
|
462
594
|
debug_default("watching project root");
|
|
463
595
|
return true;
|
|
464
596
|
}
|
|
465
|
-
const relativePath =
|
|
597
|
+
const relativePath = relative2(this.#cwd, absolutePath);
|
|
466
598
|
if (this.#isExcluded(relativePath)) {
|
|
467
599
|
debug_default('watching "%s"', absolutePath);
|
|
468
600
|
return false;
|
|
469
601
|
}
|
|
470
602
|
return true;
|
|
471
603
|
});
|
|
604
|
+
/**
|
|
605
|
+
* Create a new FileSystem instance
|
|
606
|
+
*
|
|
607
|
+
* @param cwd - The current working directory URL or string path
|
|
608
|
+
* @param tsConfig - Parsed TypeScript configuration
|
|
609
|
+
* @param rcFile - AdonisJS RC file configuration
|
|
610
|
+
*/
|
|
472
611
|
constructor(cwd, tsConfig, rcFile) {
|
|
473
|
-
this.#cwd =
|
|
612
|
+
this.#cwd = cwd;
|
|
474
613
|
this.#tsConfig = tsConfig;
|
|
475
614
|
const files = tsConfig.fileNames;
|
|
476
615
|
const metaFiles = rcFile.metaFiles ?? [];
|
|
477
616
|
const testSuites = rcFile.suites ?? [];
|
|
478
617
|
const outDir = tsConfig.raw.compilerOptions?.outDir;
|
|
479
|
-
|
|
618
|
+
for (const file of files) {
|
|
619
|
+
this.#scannedTypeScriptFiles.add(string2.toUnixSlash(file));
|
|
620
|
+
}
|
|
480
621
|
this.#includes = tsConfig.raw.include || DEFAULT_INCLUDES;
|
|
481
622
|
this.#excludes = ALWAYS_EXCLUDE.concat(
|
|
482
623
|
tsConfig.raw.exclude || (outDir ? DEFAULT_EXCLUDES.concat(outDir) : DEFAULT_EXCLUDES)
|
|
483
624
|
);
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
625
|
+
const metaFilesWithReloads = [];
|
|
626
|
+
const metaFilesWithoutReloads = [];
|
|
627
|
+
for (const file of metaFiles) {
|
|
628
|
+
if (file.reloadServer) {
|
|
629
|
+
metaFilesWithReloads.push(file.pattern);
|
|
630
|
+
} else {
|
|
631
|
+
metaFilesWithoutReloads.push(file.pattern);
|
|
488
632
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
);
|
|
496
|
-
this.#
|
|
497
|
-
testSuites.flatMap((suite) => suite.files),
|
|
498
|
-
{
|
|
499
|
-
cwd: this.#cwd
|
|
500
|
-
}
|
|
501
|
-
);
|
|
502
|
-
this.#isIncluded = picomatch(this.#includes, {
|
|
503
|
-
cwd: this.#cwd
|
|
504
|
-
});
|
|
505
|
-
this.#isExcluded = picomatch(this.#excludes, {
|
|
506
|
-
cwd: this.#cwd
|
|
507
|
-
});
|
|
633
|
+
}
|
|
634
|
+
const testFilePatterns = testSuites.flatMap((suite) => suite.files);
|
|
635
|
+
const picomatcchOptions = { cwd: this.#cwd };
|
|
636
|
+
this.#isMetaFileWithReloadsEnabled = picomatch(metaFilesWithReloads, picomatcchOptions);
|
|
637
|
+
this.#isMetaFileWithReloadsDisabled = picomatch(metaFilesWithoutReloads, picomatcchOptions);
|
|
638
|
+
this.#isTestFile = picomatch(testFilePatterns, picomatcchOptions);
|
|
639
|
+
this.#isIncluded = picomatch(this.#includes, picomatcchOptions);
|
|
640
|
+
this.#isExcluded = picomatch(this.#excludes, picomatcchOptions);
|
|
508
641
|
debug_default("initiating file system %O", {
|
|
509
642
|
includes: this.#includes,
|
|
510
|
-
excludes: this.#
|
|
643
|
+
excludes: this.#excludes,
|
|
511
644
|
outDir,
|
|
512
645
|
files,
|
|
513
646
|
metaFiles,
|
|
@@ -515,11 +648,15 @@ var FileSystem = class {
|
|
|
515
648
|
});
|
|
516
649
|
}
|
|
517
650
|
/**
|
|
518
|
-
*
|
|
651
|
+
* Determines if a file path represents a script file based on TypeScript configuration.
|
|
519
652
|
*
|
|
520
|
-
*
|
|
521
|
-
* - Files ending with ".
|
|
522
|
-
* - Files ending with ".
|
|
653
|
+
* Script files are those that can be processed by the TypeScript compiler:
|
|
654
|
+
* - Files ending with ".ts" or ".tsx" (excluding ".d.ts" declaration files)
|
|
655
|
+
* - Files ending with ".js" when "allowJs" option is enabled in tsconfig
|
|
656
|
+
* - Files ending with ".json" when "resolveJsonModule" option is enabled in tsconfig
|
|
657
|
+
*
|
|
658
|
+
* @param relativePath - The relative file path to check
|
|
659
|
+
* @returns True if the file is a script file
|
|
523
660
|
*/
|
|
524
661
|
#isScriptFile(relativePath) {
|
|
525
662
|
if ((relativePath.endsWith(".ts") || relativePath.endsWith(".tsx")) && !relativePath.endsWith(".d.ts")) {
|
|
@@ -534,9 +671,13 @@ var FileSystem = class {
|
|
|
534
671
|
return false;
|
|
535
672
|
}
|
|
536
673
|
/**
|
|
537
|
-
*
|
|
538
|
-
*
|
|
539
|
-
*
|
|
674
|
+
* Checks if a file path is part of the backend TypeScript project.
|
|
675
|
+
*
|
|
676
|
+
* Uses TypeScript configuration "includes", "excludes", and "files" paths
|
|
677
|
+
* to determine if a file should be considered part of the project compilation.
|
|
678
|
+
*
|
|
679
|
+
* @param relativePath - The relative file path to check
|
|
680
|
+
* @returns True if the file is part of the backend project
|
|
540
681
|
*/
|
|
541
682
|
#isPartOfBackendProject(relativePath) {
|
|
542
683
|
if (this.#isExcluded(relativePath)) {
|
|
@@ -555,18 +696,36 @@ var FileSystem = class {
|
|
|
555
696
|
*
|
|
556
697
|
* You must use "shouldWatchDirectory" method for directories and call
|
|
557
698
|
* this method for files only.
|
|
699
|
+
*
|
|
700
|
+
* @param absolutePath - The absolute path to the file
|
|
701
|
+
* @returns True if the file should be watched
|
|
558
702
|
*/
|
|
559
703
|
shouldWatchFile(absolutePath) {
|
|
560
|
-
return this.inspect(
|
|
704
|
+
return this.inspect(absolutePath) !== null;
|
|
561
705
|
}
|
|
562
706
|
};
|
|
563
707
|
|
|
564
708
|
// src/shortcuts_manager.ts
|
|
565
709
|
var ShortcutsManager = class {
|
|
710
|
+
/**
|
|
711
|
+
* Logger instance for displaying messages
|
|
712
|
+
*/
|
|
566
713
|
#logger;
|
|
714
|
+
/**
|
|
715
|
+
* Callback functions for different keyboard shortcuts
|
|
716
|
+
*/
|
|
567
717
|
#callbacks;
|
|
718
|
+
/**
|
|
719
|
+
* The server URL used for opening browser
|
|
720
|
+
*/
|
|
568
721
|
#serverUrl;
|
|
722
|
+
/**
|
|
723
|
+
* Key press event handler function
|
|
724
|
+
*/
|
|
569
725
|
#keyPressHandler;
|
|
726
|
+
/**
|
|
727
|
+
* Available keyboard shortcuts with their handlers
|
|
728
|
+
*/
|
|
570
729
|
#shortcuts = [
|
|
571
730
|
{
|
|
572
731
|
key: "r",
|
|
@@ -596,18 +755,32 @@ var ShortcutsManager = class {
|
|
|
596
755
|
handler: () => this.showHelp()
|
|
597
756
|
}
|
|
598
757
|
];
|
|
758
|
+
/**
|
|
759
|
+
* Create a new ShortcutsManager instance
|
|
760
|
+
*
|
|
761
|
+
* @param options - Configuration options for the shortcuts manager
|
|
762
|
+
*/
|
|
599
763
|
constructor(options) {
|
|
600
764
|
this.#logger = options.logger;
|
|
601
765
|
this.#callbacks = options.callbacks;
|
|
602
766
|
}
|
|
603
767
|
/**
|
|
604
768
|
* Set server url for opening in browser
|
|
769
|
+
*
|
|
770
|
+
* This URL will be used when the user presses 'o' to open the
|
|
771
|
+
* development server in their default browser.
|
|
772
|
+
*
|
|
773
|
+
* @param url - The server URL to open when 'o' key is pressed
|
|
605
774
|
*/
|
|
606
775
|
setServerUrl(url) {
|
|
607
776
|
this.#serverUrl = url;
|
|
608
777
|
}
|
|
609
778
|
/**
|
|
610
|
-
* Initialize keyboard shortcuts
|
|
779
|
+
* Initialize keyboard shortcuts by setting up raw mode on stdin
|
|
780
|
+
*
|
|
781
|
+
* This method enables raw mode on stdin to capture individual keypresses
|
|
782
|
+
* and sets up the event listener for handling keyboard input. Only works
|
|
783
|
+
* in TTY environments.
|
|
611
784
|
*/
|
|
612
785
|
setup() {
|
|
613
786
|
if (!process.stdin.isTTY) {
|
|
@@ -618,7 +791,12 @@ var ShortcutsManager = class {
|
|
|
618
791
|
process.stdin.on("data", this.#keyPressHandler);
|
|
619
792
|
}
|
|
620
793
|
/**
|
|
621
|
-
* Handle key press events
|
|
794
|
+
* Handle key press events and execute corresponding shortcuts
|
|
795
|
+
*
|
|
796
|
+
* Processes individual key presses and matches them against registered
|
|
797
|
+
* shortcuts. Also handles special key combinations like Ctrl+C and Ctrl+D.
|
|
798
|
+
*
|
|
799
|
+
* @param key - The pressed key as a string
|
|
622
800
|
*/
|
|
623
801
|
#handleKeyPress(key) {
|
|
624
802
|
if (key === "" || key === "") {
|
|
@@ -630,7 +808,10 @@ var ShortcutsManager = class {
|
|
|
630
808
|
}
|
|
631
809
|
}
|
|
632
810
|
/**
|
|
633
|
-
* Handle opening browser
|
|
811
|
+
* Handle opening browser with the configured server URL
|
|
812
|
+
*
|
|
813
|
+
* Uses the 'open' package to launch the default browser and navigate
|
|
814
|
+
* to the development server URL.
|
|
634
815
|
*/
|
|
635
816
|
async #handleOpenBrowser() {
|
|
636
817
|
this.#logger.log("");
|
|
@@ -639,7 +820,11 @@ var ShortcutsManager = class {
|
|
|
639
820
|
open(this.#serverUrl);
|
|
640
821
|
}
|
|
641
822
|
/**
|
|
642
|
-
* Show available keyboard shortcuts
|
|
823
|
+
* Show available keyboard shortcuts in the console
|
|
824
|
+
*
|
|
825
|
+
* Displays a formatted list of all available keyboard shortcuts
|
|
826
|
+
* and their descriptions to help users understand what actions
|
|
827
|
+
* are available.
|
|
643
828
|
*/
|
|
644
829
|
showHelp() {
|
|
645
830
|
this.#logger.log("");
|
|
@@ -647,29 +832,58 @@ var ShortcutsManager = class {
|
|
|
647
832
|
this.#shortcuts.forEach(({ key, description }) => this.#logger.log(`\xB7 ${key}: ${description}`));
|
|
648
833
|
}
|
|
649
834
|
/**
|
|
650
|
-
* Cleanup keyboard shortcuts
|
|
835
|
+
* Cleanup keyboard shortcuts and restore terminal state
|
|
836
|
+
*
|
|
837
|
+
* Disables raw mode on stdin, removes event listeners, and restores
|
|
838
|
+
* the terminal to its normal state. Should be called when shutting down
|
|
839
|
+
* the development server.
|
|
651
840
|
*/
|
|
652
841
|
cleanup() {
|
|
653
842
|
if (!process.stdin.isTTY) {
|
|
654
843
|
return;
|
|
655
844
|
}
|
|
656
845
|
process.stdin.setRawMode(false);
|
|
846
|
+
process.stdin.pause();
|
|
847
|
+
process.stdin.unref();
|
|
657
848
|
process.stdin.removeListener("data", this.#keyPressHandler);
|
|
658
849
|
this.#keyPressHandler = void 0;
|
|
659
850
|
}
|
|
660
851
|
};
|
|
661
852
|
|
|
662
853
|
// src/dev_server.ts
|
|
663
|
-
var DevServer = class {
|
|
664
|
-
constructor(cwd, options) {
|
|
665
|
-
this.cwd = cwd;
|
|
666
|
-
this.options = options;
|
|
667
|
-
this.#cwdPath = fileURLToPath4(this.cwd);
|
|
668
|
-
}
|
|
854
|
+
var DevServer = class _DevServer {
|
|
669
855
|
/**
|
|
670
|
-
*
|
|
856
|
+
* Pre-allocated info object for hot-hook change events to avoid repeated object creation
|
|
671
857
|
*/
|
|
672
|
-
#
|
|
858
|
+
static #HOT_HOOK_CHANGE_INFO = {
|
|
859
|
+
source: "hot-hook",
|
|
860
|
+
fullReload: false,
|
|
861
|
+
hotReloaded: false
|
|
862
|
+
};
|
|
863
|
+
/**
|
|
864
|
+
* Pre-allocated info object for hot-hook full reload events
|
|
865
|
+
*/
|
|
866
|
+
static #HOT_HOOK_FULL_RELOAD_INFO = {
|
|
867
|
+
source: "hot-hook",
|
|
868
|
+
fullReload: true,
|
|
869
|
+
hotReloaded: false
|
|
870
|
+
};
|
|
871
|
+
/**
|
|
872
|
+
* Pre-allocated info object for hot-hook invalidation events
|
|
873
|
+
*/
|
|
874
|
+
static #HOT_HOOK_INVALIDATED_INFO = {
|
|
875
|
+
source: "hot-hook",
|
|
876
|
+
fullReload: false,
|
|
877
|
+
hotReloaded: true
|
|
878
|
+
};
|
|
879
|
+
/**
|
|
880
|
+
* Pre-allocated info object for file watcher events
|
|
881
|
+
*/
|
|
882
|
+
static #WATCHER_INFO = {
|
|
883
|
+
source: "watcher",
|
|
884
|
+
fullReload: true,
|
|
885
|
+
hotReloaded: false
|
|
886
|
+
};
|
|
673
887
|
/**
|
|
674
888
|
* External listeners that are invoked when child process
|
|
675
889
|
* gets an error or closes
|
|
@@ -694,7 +908,7 @@ var DevServer = class {
|
|
|
694
908
|
*/
|
|
695
909
|
#httpServer;
|
|
696
910
|
/**
|
|
697
|
-
* Keyboard shortcuts manager
|
|
911
|
+
* Keyboard shortcuts manager instance
|
|
698
912
|
*/
|
|
699
913
|
#shortcutsManager;
|
|
700
914
|
/**
|
|
@@ -702,10 +916,18 @@ var DevServer = class {
|
|
|
702
916
|
* using hot-hook
|
|
703
917
|
*/
|
|
704
918
|
#fileSystem;
|
|
919
|
+
/**
|
|
920
|
+
* Index generator for managing auto-generated index files
|
|
921
|
+
*/
|
|
922
|
+
#indexGenerator;
|
|
705
923
|
/**
|
|
706
924
|
* Hooks to execute custom actions during the dev server lifecycle
|
|
707
925
|
*/
|
|
708
926
|
#hooks;
|
|
927
|
+
/**
|
|
928
|
+
* CLI UI instance for displaying colorful messages and progress information
|
|
929
|
+
*/
|
|
930
|
+
ui = cliui2();
|
|
709
931
|
/**
|
|
710
932
|
* Restarts the HTTP server and throttle concurrent calls to
|
|
711
933
|
* ensure we do not end up with a long loop of restarts
|
|
@@ -718,7 +940,10 @@ var DevServer = class {
|
|
|
718
940
|
await this.#startHTTPServer(this.#stickyPort);
|
|
719
941
|
}, "restartHTTPServer");
|
|
720
942
|
/**
|
|
721
|
-
* Sets up keyboard shortcuts
|
|
943
|
+
* Sets up keyboard shortcuts for development server interactions
|
|
944
|
+
*
|
|
945
|
+
* Initializes the shortcuts manager with callbacks for restarting the server,
|
|
946
|
+
* clearing the screen, and quitting the application.
|
|
722
947
|
*/
|
|
723
948
|
#setupKeyboardShortcuts() {
|
|
724
949
|
this.#shortcutsManager = new ShortcutsManager({
|
|
@@ -732,15 +957,14 @@ var DevServer = class {
|
|
|
732
957
|
this.#shortcutsManager.setup();
|
|
733
958
|
}
|
|
734
959
|
/**
|
|
735
|
-
* Cleanup keyboard shortcuts
|
|
960
|
+
* Cleanup keyboard shortcuts and restore terminal state
|
|
961
|
+
*
|
|
962
|
+
* Removes keyboard shortcuts event listeners and restores the terminal
|
|
963
|
+
* to its normal state when shutting down the development server.
|
|
736
964
|
*/
|
|
737
965
|
#cleanupKeyboardShortcuts() {
|
|
738
966
|
this.#shortcutsManager?.cleanup();
|
|
739
967
|
}
|
|
740
|
-
/**
|
|
741
|
-
* CLI UI to log colorful messages
|
|
742
|
-
*/
|
|
743
|
-
ui = cliui2();
|
|
744
968
|
/**
|
|
745
969
|
* The mode in which the DevServer is running.
|
|
746
970
|
*/
|
|
@@ -752,17 +976,51 @@ var DevServer = class {
|
|
|
752
976
|
*/
|
|
753
977
|
scriptFile = "bin/server.ts";
|
|
754
978
|
/**
|
|
755
|
-
*
|
|
979
|
+
* The current working directory URL
|
|
980
|
+
*/
|
|
981
|
+
cwd;
|
|
982
|
+
/**
|
|
983
|
+
* File path computed from the cwd
|
|
984
|
+
*/
|
|
985
|
+
cwdPath;
|
|
986
|
+
/**
|
|
987
|
+
* Development server configuration options including hooks and environment variables
|
|
988
|
+
*/
|
|
989
|
+
options;
|
|
990
|
+
/**
|
|
991
|
+
* Create a new DevServer instance
|
|
992
|
+
*
|
|
993
|
+
* @param cwd - The current working directory URL
|
|
994
|
+
* @param options - Development server configuration options
|
|
995
|
+
*/
|
|
996
|
+
constructor(cwd, options) {
|
|
997
|
+
this.cwd = cwd;
|
|
998
|
+
this.options = options;
|
|
999
|
+
this.cwdPath = string3.toUnixSlash(fileURLToPath2(this.cwd));
|
|
1000
|
+
}
|
|
1001
|
+
/**
|
|
1002
|
+
* Type guard to check if child process message is from AdonisJS HTTP server
|
|
1003
|
+
*
|
|
1004
|
+
* Validates that a message from the child process contains the expected
|
|
1005
|
+
* structure indicating the AdonisJS server is ready and listening.
|
|
1006
|
+
*
|
|
1007
|
+
* @param message - Unknown message from child process
|
|
1008
|
+
* @returns True if message is an AdonisJS ready message
|
|
756
1009
|
*/
|
|
757
1010
|
#isAdonisJSReadyMessage(message) {
|
|
758
1011
|
return message !== null && typeof message === "object" && "isAdonisJS" in message && "environment" in message && message.environment === "web";
|
|
759
1012
|
}
|
|
760
1013
|
/**
|
|
761
|
-
* Displays
|
|
762
|
-
*
|
|
1014
|
+
* Displays server information and executes hooks after server startup
|
|
1015
|
+
*
|
|
1016
|
+
* Shows server URL, mode, startup duration, and help instructions.
|
|
1017
|
+
* Also executes the devServerStarted hooks to allow custom post-startup logic.
|
|
1018
|
+
*
|
|
1019
|
+
* @param message - Server ready message containing port, host, and optional duration
|
|
763
1020
|
*/
|
|
764
1021
|
async #postServerReady(message) {
|
|
765
1022
|
const host = message.host === "0.0.0.0" ? "127.0.0.1" : message.host;
|
|
1023
|
+
const info = { host, port: message.port };
|
|
766
1024
|
const serverUrl = `http://${host}:${message.port}`;
|
|
767
1025
|
this.#shortcutsManager?.setServerUrl(serverUrl);
|
|
768
1026
|
const displayMessage = this.ui.sticker().add(`Server address: ${this.ui.colors.cyan(serverUrl)}`).add(`Mode: ${this.ui.colors.cyan(this.mode)}`);
|
|
@@ -771,7 +1029,7 @@ var DevServer = class {
|
|
|
771
1029
|
}
|
|
772
1030
|
displayMessage.add(`Press ${this.ui.colors.dim("h")} to show help`);
|
|
773
1031
|
try {
|
|
774
|
-
await this.#hooks.runner("devServerStarted").run(this, displayMessage);
|
|
1032
|
+
await this.#hooks.runner("devServerStarted").run(this, info, displayMessage);
|
|
775
1033
|
} catch (error) {
|
|
776
1034
|
this.ui.logger.error('One of the "devServerStarted" hooks failed');
|
|
777
1035
|
this.ui.logger.fatal(error);
|
|
@@ -779,13 +1037,22 @@ var DevServer = class {
|
|
|
779
1037
|
displayMessage.render();
|
|
780
1038
|
}
|
|
781
1039
|
/**
|
|
782
|
-
*
|
|
1040
|
+
* Type guard to check if child process message is from hot-hook
|
|
1041
|
+
*
|
|
1042
|
+
* Validates that a message from the child process is a hot-hook notification
|
|
1043
|
+
* about file changes, invalidations, or full reloads.
|
|
1044
|
+
*
|
|
1045
|
+
* @param message - Unknown message from child process
|
|
1046
|
+
* @returns True if message is a hot-hook message
|
|
783
1047
|
*/
|
|
784
1048
|
#isHotHookMessage(message) {
|
|
785
1049
|
return message !== null && typeof message === "object" && "type" in message && typeof message.type === "string" && message.type.startsWith("hot-hook:");
|
|
786
1050
|
}
|
|
787
1051
|
/**
|
|
788
|
-
* Conditionally
|
|
1052
|
+
* Conditionally clears the terminal screen based on configuration
|
|
1053
|
+
*
|
|
1054
|
+
* Clears the terminal screen if the clearScreen option is enabled,
|
|
1055
|
+
* providing a clean view for development output.
|
|
789
1056
|
*/
|
|
790
1057
|
#clearScreen() {
|
|
791
1058
|
if (this.options.clearScreen) {
|
|
@@ -793,48 +1060,120 @@ var DevServer = class {
|
|
|
793
1060
|
}
|
|
794
1061
|
}
|
|
795
1062
|
/**
|
|
796
|
-
* Handles file change
|
|
1063
|
+
* Handles file change events and triggers appropriate server actions
|
|
1064
|
+
*
|
|
1065
|
+
* Processes file change notifications and determines whether to restart
|
|
1066
|
+
* the server, hot reload, or ignore the change based on file type and mode.
|
|
1067
|
+
*
|
|
1068
|
+
* @param relativePath - Relative path to the changed file
|
|
1069
|
+
* @param absolutePath - Absolute path to the changed file
|
|
1070
|
+
* @param action - Type of file change (add, update, delete)
|
|
1071
|
+
* @param info - Optional information about the change source and reload behavior
|
|
797
1072
|
*/
|
|
798
|
-
#handleFileChange(
|
|
1073
|
+
#handleFileChange(relativePath, absolutePath, action, info) {
|
|
799
1074
|
if ((action === "add" || action === "delete") && this.mode === "hmr") {
|
|
800
|
-
debug_default("ignoring add and delete actions in HMR mode %s",
|
|
1075
|
+
debug_default("ignoring add and delete actions in HMR mode %s", relativePath);
|
|
801
1076
|
return;
|
|
802
1077
|
}
|
|
803
1078
|
if (info && info.source === "hot-hook" && info.hotReloaded) {
|
|
804
|
-
debug_default("hot reloading %s, info %O",
|
|
805
|
-
this.ui.logger.log(`${this.ui.colors.green("invalidated")} ${
|
|
1079
|
+
debug_default("hot reloading %s, info %O", relativePath, info);
|
|
1080
|
+
this.ui.logger.log(`${this.ui.colors.green("invalidated")} ${relativePath}`);
|
|
806
1081
|
return;
|
|
807
1082
|
}
|
|
808
1083
|
if (info && !info.fullReload) {
|
|
809
|
-
debug_default("ignoring full reload",
|
|
1084
|
+
debug_default("ignoring full reload", relativePath, info);
|
|
810
1085
|
return;
|
|
811
1086
|
}
|
|
812
|
-
const file = this.#fileSystem.inspect(
|
|
1087
|
+
const file = this.#fileSystem.inspect(absolutePath, relativePath);
|
|
813
1088
|
if (!file) {
|
|
814
1089
|
return;
|
|
815
1090
|
}
|
|
816
1091
|
if (file.reloadServer) {
|
|
817
1092
|
this.#clearScreen();
|
|
818
|
-
this.ui.logger.log(`${this.ui.colors.green(action)} ${
|
|
1093
|
+
this.ui.logger.log(`${this.ui.colors.green(action)} ${relativePath}`);
|
|
819
1094
|
this.#restartHTTPServer();
|
|
820
1095
|
return;
|
|
821
1096
|
}
|
|
822
|
-
this.ui.logger.log(`${this.ui.colors.green(action)} ${
|
|
1097
|
+
this.ui.logger.log(`${this.ui.colors.green(action)} ${relativePath}`);
|
|
823
1098
|
}
|
|
824
1099
|
/**
|
|
825
|
-
*
|
|
826
|
-
*
|
|
1100
|
+
* Regenerates index files when a file is added or removed
|
|
1101
|
+
*
|
|
1102
|
+
* Updates the index generator to reflect file system changes by adding
|
|
1103
|
+
* or removing files from the generated index files.
|
|
1104
|
+
*
|
|
1105
|
+
* @param filePath - Absolute path to the file that changed
|
|
1106
|
+
* @param action - Whether the file was added or deleted
|
|
1107
|
+
*/
|
|
1108
|
+
#regenerateIndex(filePath, action) {
|
|
1109
|
+
if (action === "add") {
|
|
1110
|
+
return this.#indexGenerator.addFile(filePath);
|
|
1111
|
+
}
|
|
1112
|
+
return this.#indexGenerator.removeFile(filePath);
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* Registers hooks for file system events and server restart triggers
|
|
1116
|
+
*
|
|
1117
|
+
* Sets up event handlers that respond to file additions, changes, and removals
|
|
1118
|
+
* by regenerating indexes and handling server restarts as needed.
|
|
827
1119
|
*/
|
|
828
1120
|
#registerServerRestartHooks() {
|
|
829
|
-
this.#hooks.add("fileAdded", (
|
|
1121
|
+
this.#hooks.add("fileAdded", (relativePath, absolutePath) => {
|
|
1122
|
+
this.#regenerateIndex(absolutePath, "add");
|
|
1123
|
+
this.#handleFileChange(relativePath, absolutePath, "add");
|
|
1124
|
+
});
|
|
830
1125
|
this.#hooks.add(
|
|
831
1126
|
"fileChanged",
|
|
832
|
-
(
|
|
1127
|
+
(relativePath, absolutePath, info) => this.#handleFileChange(relativePath, absolutePath, "update", info)
|
|
833
1128
|
);
|
|
834
|
-
this.#hooks.add("fileRemoved", (
|
|
1129
|
+
this.#hooks.add("fileRemoved", (relativePath, absolutePath) => {
|
|
1130
|
+
this.#regenerateIndex(absolutePath, "delete");
|
|
1131
|
+
this.#handleFileChange(relativePath, absolutePath, "delete");
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* Initiate the state for DevServer and executes the init hooks
|
|
1136
|
+
*/
|
|
1137
|
+
async #init(ts, mode) {
|
|
1138
|
+
const tsConfig = parseConfig(this.cwd, ts);
|
|
1139
|
+
if (!tsConfig) {
|
|
1140
|
+
this.#onError?.(new RuntimeException("Unable to parse tsconfig file"));
|
|
1141
|
+
return false;
|
|
1142
|
+
}
|
|
1143
|
+
this.#mode = mode;
|
|
1144
|
+
this.#clearScreen();
|
|
1145
|
+
this.ui.logger.info(`starting server in ${this.#mode} mode...`);
|
|
1146
|
+
this.#indexGenerator = new IndexGenerator(this.cwdPath, this.ui.logger);
|
|
1147
|
+
this.#stickyPort = String(await getPort(this.cwd));
|
|
1148
|
+
this.#fileSystem = new FileSystem(this.cwdPath, tsConfig, this.options);
|
|
1149
|
+
this.ui.logger.info("loading hooks...");
|
|
1150
|
+
this.#hooks = await loadHooks(this.options.hooks, [
|
|
1151
|
+
"init",
|
|
1152
|
+
"routesCommitted",
|
|
1153
|
+
"routesScanning",
|
|
1154
|
+
"routesScanned",
|
|
1155
|
+
"devServerStarting",
|
|
1156
|
+
"devServerStarted",
|
|
1157
|
+
"fileAdded",
|
|
1158
|
+
"fileChanged",
|
|
1159
|
+
"fileRemoved"
|
|
1160
|
+
]);
|
|
1161
|
+
this.#registerServerRestartHooks();
|
|
1162
|
+
this.#setupKeyboardShortcuts();
|
|
1163
|
+
await this.#hooks.runner("init").run(this, this.#indexGenerator);
|
|
1164
|
+
this.#hooks.clear("init");
|
|
1165
|
+
this.ui.logger.info("generating indexes...");
|
|
1166
|
+
await this.#indexGenerator.generate();
|
|
1167
|
+
return true;
|
|
835
1168
|
}
|
|
836
1169
|
/**
|
|
837
|
-
* Starts the HTTP server
|
|
1170
|
+
* Starts the HTTP server as a child process
|
|
1171
|
+
*
|
|
1172
|
+
* Creates a new Node.js child process to run the server script with the
|
|
1173
|
+
* specified port and configuration. Sets up message handlers for server
|
|
1174
|
+
* ready notifications and hot-hook events.
|
|
1175
|
+
*
|
|
1176
|
+
* @param port - Port number for the server to listen on
|
|
838
1177
|
*/
|
|
839
1178
|
async #startHTTPServer(port) {
|
|
840
1179
|
await this.#hooks.runner("devServerStarting").run(this);
|
|
@@ -854,45 +1193,21 @@ var DevServer = class {
|
|
|
854
1193
|
resolve();
|
|
855
1194
|
} else if (this.#mode === "hmr" && this.#isHotHookMessage(message)) {
|
|
856
1195
|
debug_default("received hot-hook message %O", message);
|
|
1196
|
+
const absolutePath = message.path ? string3.toUnixSlash(message.path) : "";
|
|
1197
|
+
const relativePath = relative3(this.cwdPath, absolutePath);
|
|
857
1198
|
if (message.type === "hot-hook:file-changed") {
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
{
|
|
866
|
-
source: "hot-hook",
|
|
867
|
-
fullReload: false,
|
|
868
|
-
hotReloaded: false
|
|
869
|
-
},
|
|
870
|
-
this
|
|
871
|
-
);
|
|
872
|
-
break;
|
|
873
|
-
case "unlink":
|
|
874
|
-
this.#hooks.runner("fileRemoved").run(string3.toUnixSlash(relative4(this.#cwdPath, message.path)), this);
|
|
1199
|
+
const { action } = message;
|
|
1200
|
+
if (action === "add") {
|
|
1201
|
+
this.#hooks.runner("fileAdded").run(relativePath, absolutePath, this);
|
|
1202
|
+
} else if (action === "change") {
|
|
1203
|
+
this.#hooks.runner("fileChanged").run(relativePath, absolutePath, _DevServer.#HOT_HOOK_CHANGE_INFO, this);
|
|
1204
|
+
} else if (action === "unlink") {
|
|
1205
|
+
this.#hooks.runner("fileRemoved").run(relativePath, absolutePath, this);
|
|
875
1206
|
}
|
|
876
1207
|
} else if (message.type === "hot-hook:full-reload") {
|
|
877
|
-
this.#hooks.runner("fileChanged").run(
|
|
878
|
-
string3.toUnixSlash(relative4(this.#cwdPath, message.path)),
|
|
879
|
-
{
|
|
880
|
-
source: "hot-hook",
|
|
881
|
-
fullReload: true,
|
|
882
|
-
hotReloaded: false
|
|
883
|
-
},
|
|
884
|
-
this
|
|
885
|
-
);
|
|
1208
|
+
this.#hooks.runner("fileChanged").run(relativePath, absolutePath, _DevServer.#HOT_HOOK_FULL_RELOAD_INFO, this);
|
|
886
1209
|
} else if (message.type === "hot-hook:invalidated") {
|
|
887
|
-
this.#hooks.runner("fileChanged").run(
|
|
888
|
-
string3.toUnixSlash(relative4(this.#cwdPath, message.path)),
|
|
889
|
-
{
|
|
890
|
-
source: "hot-hook",
|
|
891
|
-
fullReload: false,
|
|
892
|
-
hotReloaded: true
|
|
893
|
-
},
|
|
894
|
-
this
|
|
895
|
-
);
|
|
1210
|
+
this.#hooks.runner("fileChanged").run(relativePath, absolutePath, _DevServer.#HOT_HOOK_INVALIDATED_INFO, this);
|
|
896
1211
|
}
|
|
897
1212
|
}
|
|
898
1213
|
});
|
|
@@ -914,16 +1229,20 @@ var DevServer = class {
|
|
|
914
1229
|
});
|
|
915
1230
|
}
|
|
916
1231
|
/**
|
|
917
|
-
* Add listener to get notified when dev server is
|
|
918
|
-
*
|
|
1232
|
+
* Add listener to get notified when dev server is closed
|
|
1233
|
+
*
|
|
1234
|
+
* @param callback - Function to call when dev server closes
|
|
1235
|
+
* @returns This DevServer instance for method chaining
|
|
919
1236
|
*/
|
|
920
1237
|
onClose(callback) {
|
|
921
1238
|
this.#onClose = callback;
|
|
922
1239
|
return this;
|
|
923
1240
|
}
|
|
924
1241
|
/**
|
|
925
|
-
* Add listener to get notified when dev server
|
|
926
|
-
*
|
|
1242
|
+
* Add listener to get notified when dev server encounters an error
|
|
1243
|
+
*
|
|
1244
|
+
* @param callback - Function to call when dev server encounters an error
|
|
1245
|
+
* @returns This DevServer instance for method chaining
|
|
927
1246
|
*/
|
|
928
1247
|
onError(callback) {
|
|
929
1248
|
this.#onError = callback;
|
|
@@ -941,27 +1260,17 @@ var DevServer = class {
|
|
|
941
1260
|
}
|
|
942
1261
|
}
|
|
943
1262
|
/**
|
|
944
|
-
* Start the development server
|
|
1263
|
+
* Start the development server in static or HMR mode
|
|
1264
|
+
*
|
|
1265
|
+
* @param ts - TypeScript module reference
|
|
945
1266
|
*/
|
|
946
1267
|
async start(ts) {
|
|
947
|
-
const
|
|
948
|
-
if (!
|
|
949
|
-
this.#onError?.(new RuntimeException("Unable to parse tsconfig file"));
|
|
1268
|
+
const initiated = await this.#init(ts, this.options.hmr ? "hmr" : "static");
|
|
1269
|
+
if (!initiated) {
|
|
950
1270
|
return;
|
|
951
1271
|
}
|
|
952
|
-
this.#
|
|
953
|
-
|
|
954
|
-
this.#hooks = await loadHooks(this.options.hooks, [
|
|
955
|
-
"devServerStarting",
|
|
956
|
-
"devServerStarted",
|
|
957
|
-
"fileAdded",
|
|
958
|
-
"fileChanged",
|
|
959
|
-
"fileRemoved"
|
|
960
|
-
]);
|
|
961
|
-
this.#registerServerRestartHooks();
|
|
962
|
-
if (this.options.hmr) {
|
|
963
|
-
this.#mode = "hmr";
|
|
964
|
-
this.options.nodeArgs = this.options.nodeArgs.concat("--import=hot-hook/register");
|
|
1272
|
+
if (this.#mode === "hmr") {
|
|
1273
|
+
this.options.nodeArgs.push("--import=hot-hook/register");
|
|
965
1274
|
this.options.env = {
|
|
966
1275
|
...this.options.env,
|
|
967
1276
|
HOT_HOOK_INCLUDE: this.#fileSystem.includes.join(","),
|
|
@@ -969,38 +1278,25 @@ var DevServer = class {
|
|
|
969
1278
|
HOT_HOOK_RESTART: (this.options.metaFiles ?? []).filter(({ reloadServer }) => !!reloadServer).map(({ pattern }) => pattern).join(",")
|
|
970
1279
|
};
|
|
971
1280
|
}
|
|
972
|
-
this.#clearScreen();
|
|
973
|
-
this.#setupKeyboardShortcuts();
|
|
974
1281
|
this.ui.logger.info("starting HTTP server...");
|
|
975
1282
|
await this.#startHTTPServer(this.#stickyPort);
|
|
976
1283
|
}
|
|
977
1284
|
/**
|
|
978
|
-
* Start the development server in watch mode
|
|
1285
|
+
* Start the development server in watch mode and restart on file changes
|
|
1286
|
+
*
|
|
1287
|
+
* @param ts - TypeScript module reference
|
|
1288
|
+
* @param options - Watch options including polling mode
|
|
979
1289
|
*/
|
|
980
1290
|
async startAndWatch(ts, options) {
|
|
981
|
-
const
|
|
982
|
-
if (!
|
|
983
|
-
this.#onError?.(new RuntimeException("Unable to parse tsconfig file"));
|
|
1291
|
+
const initiated = await this.#init(ts, "watch");
|
|
1292
|
+
if (!initiated) {
|
|
984
1293
|
return;
|
|
985
1294
|
}
|
|
986
|
-
this.#mode = "watch";
|
|
987
|
-
this.#stickyPort = String(await getPort(this.cwd));
|
|
988
|
-
this.#fileSystem = new FileSystem(this.cwd, tsConfig, this.options);
|
|
989
|
-
this.#hooks = await loadHooks(this.options.hooks, [
|
|
990
|
-
"devServerStarting",
|
|
991
|
-
"devServerStarted",
|
|
992
|
-
"fileAdded",
|
|
993
|
-
"fileChanged",
|
|
994
|
-
"fileRemoved"
|
|
995
|
-
]);
|
|
996
|
-
this.#registerServerRestartHooks();
|
|
997
|
-
this.#clearScreen();
|
|
998
|
-
this.#setupKeyboardShortcuts();
|
|
999
1295
|
this.ui.logger.info("starting HTTP server...");
|
|
1000
1296
|
await this.#startHTTPServer(this.#stickyPort);
|
|
1001
1297
|
this.#watcher = watch({
|
|
1002
1298
|
usePolling: options?.poll ?? false,
|
|
1003
|
-
cwd: this
|
|
1299
|
+
cwd: this.cwdPath,
|
|
1004
1300
|
ignoreInitial: true,
|
|
1005
1301
|
ignored: (file, stats) => {
|
|
1006
1302
|
if (!stats) {
|
|
@@ -1021,39 +1317,31 @@ var DevServer = class {
|
|
|
1021
1317
|
this.#onError?.(error);
|
|
1022
1318
|
this.#watcher?.close();
|
|
1023
1319
|
});
|
|
1024
|
-
this.#watcher.on(
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
);
|
|
1040
|
-
this.#watcher.on(
|
|
1041
|
-
"unlink",
|
|
1042
|
-
(filePath) => this.#hooks.runner("fileRemoved").run(string3.toUnixSlash(filePath), this)
|
|
1043
|
-
);
|
|
1320
|
+
this.#watcher.on("add", (filePath) => {
|
|
1321
|
+
const relativePath = string3.toUnixSlash(filePath);
|
|
1322
|
+
const absolutePath = join2(this.cwdPath, relativePath);
|
|
1323
|
+
this.#hooks.runner("fileAdded").run(relativePath, absolutePath, this);
|
|
1324
|
+
});
|
|
1325
|
+
this.#watcher.on("change", (filePath) => {
|
|
1326
|
+
const relativePath = string3.toUnixSlash(filePath);
|
|
1327
|
+
const absolutePath = join2(this.cwdPath, relativePath);
|
|
1328
|
+
this.#hooks.runner("fileChanged").run(relativePath, absolutePath, _DevServer.#WATCHER_INFO, this);
|
|
1329
|
+
});
|
|
1330
|
+
this.#watcher.on("unlink", (filePath) => {
|
|
1331
|
+
const relativePath = string3.toUnixSlash(filePath);
|
|
1332
|
+
const absolutePath = join2(this.cwdPath, relativePath);
|
|
1333
|
+
this.#hooks.runner("fileRemoved").run(relativePath, absolutePath, this);
|
|
1334
|
+
});
|
|
1044
1335
|
}
|
|
1045
1336
|
};
|
|
1046
1337
|
|
|
1047
1338
|
// src/test_runner.ts
|
|
1339
|
+
import { join as join3 } from "path/posix";
|
|
1048
1340
|
import { cliui as cliui3 } from "@poppinss/cliui";
|
|
1049
|
-
import { fileURLToPath as
|
|
1341
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1050
1342
|
import string4 from "@poppinss/utils/string";
|
|
1051
1343
|
import { RuntimeException as RuntimeException2 } from "@poppinss/utils/exception";
|
|
1052
1344
|
var TestRunner = class {
|
|
1053
|
-
constructor(cwd, options) {
|
|
1054
|
-
this.cwd = cwd;
|
|
1055
|
-
this.options = options;
|
|
1056
|
-
}
|
|
1057
1345
|
/**
|
|
1058
1346
|
* External listeners that are invoked when child process
|
|
1059
1347
|
* gets an error or closes
|
|
@@ -1082,6 +1370,14 @@ var TestRunner = class {
|
|
|
1082
1370
|
* Hooks to execute custom actions during the tests runner lifecycle
|
|
1083
1371
|
*/
|
|
1084
1372
|
#hooks;
|
|
1373
|
+
/**
|
|
1374
|
+
* Index generator for managing auto-generated index files
|
|
1375
|
+
*/
|
|
1376
|
+
#indexGenerator;
|
|
1377
|
+
/**
|
|
1378
|
+
* CLI UI instance for displaying colorful messages and progress information
|
|
1379
|
+
*/
|
|
1380
|
+
ui = cliui3();
|
|
1085
1381
|
/**
|
|
1086
1382
|
* Re-runs the test child process and throttle concurrent calls to
|
|
1087
1383
|
* ensure we do not end up with a long loop of restarts
|
|
@@ -1093,16 +1389,40 @@ var TestRunner = class {
|
|
|
1093
1389
|
}
|
|
1094
1390
|
await this.#runTests(this.#stickyPort, filters);
|
|
1095
1391
|
}, "reRunTests");
|
|
1096
|
-
/**
|
|
1097
|
-
* CLI UI to log colorful messages
|
|
1098
|
-
*/
|
|
1099
|
-
ui = cliui3();
|
|
1100
1392
|
/**
|
|
1101
1393
|
* The script file to run as a child process
|
|
1102
1394
|
*/
|
|
1103
1395
|
scriptFile = "bin/test.ts";
|
|
1396
|
+
/**
|
|
1397
|
+
* The current working directory URL
|
|
1398
|
+
*/
|
|
1399
|
+
cwd;
|
|
1400
|
+
/**
|
|
1401
|
+
* The current working directory path as a string
|
|
1402
|
+
*/
|
|
1403
|
+
cwdPath;
|
|
1404
|
+
/**
|
|
1405
|
+
* Test runner configuration options including filters, reporters, and hooks
|
|
1406
|
+
*/
|
|
1407
|
+
options;
|
|
1408
|
+
/**
|
|
1409
|
+
* Create a new TestRunner instance
|
|
1410
|
+
*
|
|
1411
|
+
* @param cwd - The current working directory URL
|
|
1412
|
+
* @param options - Test runner configuration options
|
|
1413
|
+
*/
|
|
1414
|
+
constructor(cwd, options) {
|
|
1415
|
+
this.cwd = cwd;
|
|
1416
|
+
this.options = options;
|
|
1417
|
+
this.cwdPath = string4.toUnixSlash(fileURLToPath3(this.cwd));
|
|
1418
|
+
}
|
|
1104
1419
|
/**
|
|
1105
1420
|
* Convert test runner options to the CLI args
|
|
1421
|
+
*
|
|
1422
|
+
* Transforms the test runner configuration options into command-line
|
|
1423
|
+
* arguments that can be passed to the test script.
|
|
1424
|
+
*
|
|
1425
|
+
* @returns Array of command-line arguments
|
|
1106
1426
|
*/
|
|
1107
1427
|
#convertOptionsToArgs() {
|
|
1108
1428
|
const args = [];
|
|
@@ -1124,7 +1444,13 @@ var TestRunner = class {
|
|
|
1124
1444
|
return args;
|
|
1125
1445
|
}
|
|
1126
1446
|
/**
|
|
1127
|
-
* Converts all known filters to CLI args
|
|
1447
|
+
* Converts all known filters to CLI args
|
|
1448
|
+
*
|
|
1449
|
+
* Transforms test filters (suites, files, groups, tags, tests) into
|
|
1450
|
+
* command-line arguments for the test script.
|
|
1451
|
+
*
|
|
1452
|
+
* @param filters - The test filters to convert
|
|
1453
|
+
* @returns Array of command-line arguments representing the filters
|
|
1128
1454
|
*/
|
|
1129
1455
|
#convertFiltersToArgs(filters) {
|
|
1130
1456
|
const args = [];
|
|
@@ -1151,6 +1477,9 @@ var TestRunner = class {
|
|
|
1151
1477
|
}
|
|
1152
1478
|
/**
|
|
1153
1479
|
* Conditionally clear the terminal screen
|
|
1480
|
+
*
|
|
1481
|
+
* Clears the terminal screen if the clearScreen option is enabled
|
|
1482
|
+
* in the test runner configuration.
|
|
1154
1483
|
*/
|
|
1155
1484
|
#clearScreen() {
|
|
1156
1485
|
if (this.options.clearScreen) {
|
|
@@ -1158,18 +1487,25 @@ var TestRunner = class {
|
|
|
1158
1487
|
}
|
|
1159
1488
|
}
|
|
1160
1489
|
/**
|
|
1161
|
-
* Runs tests
|
|
1490
|
+
* Runs tests as a child process
|
|
1491
|
+
*
|
|
1492
|
+
* Creates a Node.js child process to execute the test script with
|
|
1493
|
+
* appropriate command-line arguments and environment variables.
|
|
1494
|
+
* Handles process lifecycle and hook execution.
|
|
1495
|
+
*
|
|
1496
|
+
* @param port - The port number to set in the environment
|
|
1497
|
+
* @param filters - Optional test filters to apply for this run
|
|
1162
1498
|
*/
|
|
1163
1499
|
async #runTests(port, filters) {
|
|
1164
1500
|
await this.#hooks.runner("testsStarting").run(this);
|
|
1165
1501
|
debug_default('running tests using "%s" file, options %O', this.scriptFile, this.options);
|
|
1166
1502
|
return new Promise(async (resolve) => {
|
|
1167
|
-
const
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1503
|
+
const mergedFilters = { ...this.options.filters, ...filters };
|
|
1504
|
+
const scriptArgs = [
|
|
1505
|
+
...this.#convertOptionsToArgs(),
|
|
1506
|
+
...this.options.scriptArgs,
|
|
1507
|
+
...this.#convertFiltersToArgs(mergedFilters)
|
|
1508
|
+
];
|
|
1173
1509
|
this.#testsProcess = runNode(this.cwd, {
|
|
1174
1510
|
script: this.scriptFile,
|
|
1175
1511
|
reject: true,
|
|
@@ -1198,41 +1534,73 @@ var TestRunner = class {
|
|
|
1198
1534
|
});
|
|
1199
1535
|
}
|
|
1200
1536
|
/**
|
|
1201
|
-
* Handles file change event
|
|
1537
|
+
* Handles file change event during watch mode
|
|
1538
|
+
*
|
|
1539
|
+
* Determines whether to run specific tests or all tests based on
|
|
1540
|
+
* the type of file that changed. Test files trigger selective runs,
|
|
1541
|
+
* while other files trigger full test suite runs.
|
|
1542
|
+
*
|
|
1543
|
+
* @param filePath - The path of the changed file
|
|
1544
|
+
* @param action - The type of change (add, update, delete)
|
|
1202
1545
|
*/
|
|
1203
|
-
#handleFileChange(
|
|
1204
|
-
const file = this.#fileSystem.inspect(
|
|
1546
|
+
#handleFileChange(relativePath, absolutePath, action) {
|
|
1547
|
+
const file = this.#fileSystem.inspect(absolutePath, relativePath);
|
|
1205
1548
|
if (!file) {
|
|
1206
1549
|
return;
|
|
1207
1550
|
}
|
|
1208
1551
|
this.#clearScreen();
|
|
1209
|
-
this.ui.logger.log(`${this.ui.colors.green(action)} ${
|
|
1552
|
+
this.ui.logger.log(`${this.ui.colors.green(action)} ${relativePath}`);
|
|
1210
1553
|
if (file.fileType === "test") {
|
|
1211
|
-
this.#reRunTests({ files: [
|
|
1554
|
+
this.#reRunTests({ files: [relativePath] });
|
|
1212
1555
|
} else {
|
|
1213
1556
|
this.#reRunTests();
|
|
1214
1557
|
}
|
|
1215
1558
|
}
|
|
1216
1559
|
/**
|
|
1217
|
-
*
|
|
1218
|
-
*
|
|
1560
|
+
* Re-generates the index when a file is changed, but only in HMR
|
|
1561
|
+
* mode
|
|
1562
|
+
*/
|
|
1563
|
+
#regenerateIndex(filePath, action) {
|
|
1564
|
+
if (action === "add") {
|
|
1565
|
+
return this.#indexGenerator.addFile(filePath);
|
|
1566
|
+
}
|
|
1567
|
+
return this.#indexGenerator.removeFile(filePath);
|
|
1568
|
+
}
|
|
1569
|
+
/**
|
|
1570
|
+
* Registers inline hooks for file changes and test re-runs
|
|
1571
|
+
*
|
|
1572
|
+
* Sets up event handlers that respond to file system changes by
|
|
1573
|
+
* triggering appropriate test runs based on the changed files.
|
|
1219
1574
|
*/
|
|
1220
1575
|
#registerServerRestartHooks() {
|
|
1221
|
-
this.#hooks.add("fileAdded", (
|
|
1222
|
-
|
|
1223
|
-
|
|
1576
|
+
this.#hooks.add("fileAdded", (relativePath, absolutePath) => {
|
|
1577
|
+
this.#regenerateIndex(absolutePath, "add");
|
|
1578
|
+
this.#handleFileChange(relativePath, absolutePath, "add");
|
|
1579
|
+
});
|
|
1580
|
+
this.#hooks.add("fileChanged", (relativePath, absolutePath) => {
|
|
1581
|
+
this.#regenerateIndex(absolutePath, "add");
|
|
1582
|
+
this.#handleFileChange(relativePath, absolutePath, "update");
|
|
1583
|
+
});
|
|
1584
|
+
this.#hooks.add("fileRemoved", (relativePath, absolutePath) => {
|
|
1585
|
+
this.#regenerateIndex(absolutePath, "delete");
|
|
1586
|
+
this.#handleFileChange(relativePath, absolutePath, "delete");
|
|
1587
|
+
});
|
|
1224
1588
|
}
|
|
1225
1589
|
/**
|
|
1226
|
-
* Add listener to get notified when
|
|
1227
|
-
*
|
|
1590
|
+
* Add listener to get notified when test runner is closed
|
|
1591
|
+
*
|
|
1592
|
+
* @param callback - Function to call when test runner closes
|
|
1593
|
+
* @returns This TestRunner instance for method chaining
|
|
1228
1594
|
*/
|
|
1229
1595
|
onClose(callback) {
|
|
1230
1596
|
this.#onClose = callback;
|
|
1231
1597
|
return this;
|
|
1232
1598
|
}
|
|
1233
1599
|
/**
|
|
1234
|
-
* Add listener to get notified when
|
|
1235
|
-
*
|
|
1600
|
+
* Add listener to get notified when test runner encounters an error
|
|
1601
|
+
*
|
|
1602
|
+
* @param callback - Function to call when test runner encounters an error
|
|
1603
|
+
* @returns This TestRunner instance for method chaining
|
|
1236
1604
|
*/
|
|
1237
1605
|
onError(callback) {
|
|
1238
1606
|
this.#onError = callback;
|
|
@@ -1240,6 +1608,9 @@ var TestRunner = class {
|
|
|
1240
1608
|
}
|
|
1241
1609
|
/**
|
|
1242
1610
|
* Close watchers and running child processes
|
|
1611
|
+
*
|
|
1612
|
+
* Cleans up file system watchers and terminates any running test
|
|
1613
|
+
* processes to ensure graceful shutdown.
|
|
1243
1614
|
*/
|
|
1244
1615
|
async close() {
|
|
1245
1616
|
await this.#watcher?.close();
|
|
@@ -1249,23 +1620,34 @@ var TestRunner = class {
|
|
|
1249
1620
|
}
|
|
1250
1621
|
}
|
|
1251
1622
|
/**
|
|
1252
|
-
* Runs tests
|
|
1623
|
+
* Runs tests once without watching for file changes
|
|
1624
|
+
*
|
|
1625
|
+
* Executes the test suite a single time and exits. This is the
|
|
1626
|
+
* equivalent of running tests in CI/CD environments.
|
|
1253
1627
|
*/
|
|
1254
1628
|
async run() {
|
|
1255
1629
|
this.#stickyPort = String(await getPort(this.cwd));
|
|
1256
|
-
this.#
|
|
1257
|
-
"testsStarting",
|
|
1258
|
-
"testsFinished",
|
|
1259
|
-
"fileAdded",
|
|
1260
|
-
"fileChanged",
|
|
1261
|
-
"fileRemoved"
|
|
1262
|
-
]);
|
|
1630
|
+
this.#indexGenerator = new IndexGenerator(this.cwdPath, this.ui.logger);
|
|
1263
1631
|
this.#clearScreen();
|
|
1632
|
+
this.ui.logger.info("loading hooks...");
|
|
1633
|
+
this.#hooks = await loadHooks(this.options.hooks, ["init", "testsStarting", "testsFinished"]);
|
|
1634
|
+
await this.#hooks.runner("init").run(this, this.#indexGenerator);
|
|
1635
|
+
this.#hooks.clear("init");
|
|
1636
|
+
this.ui.logger.info("generating indexes...");
|
|
1637
|
+
await this.#indexGenerator.generate();
|
|
1264
1638
|
this.ui.logger.info("booting application to run tests...");
|
|
1265
1639
|
await this.#runTests(this.#stickyPort);
|
|
1266
1640
|
}
|
|
1267
1641
|
/**
|
|
1268
|
-
* Run tests in watch mode
|
|
1642
|
+
* Run tests in watch mode and re-run them when files change
|
|
1643
|
+
*
|
|
1644
|
+
* Starts the test runner in watch mode, monitoring the file system
|
|
1645
|
+
* for changes and automatically re-running tests when relevant files
|
|
1646
|
+
* are modified. Uses intelligent filtering to run only affected tests
|
|
1647
|
+
* when possible.
|
|
1648
|
+
*
|
|
1649
|
+
* @param ts - TypeScript module reference for parsing configuration
|
|
1650
|
+
* @param options - Watch options including polling mode for file system monitoring
|
|
1269
1651
|
*/
|
|
1270
1652
|
async runAndWatch(ts, options) {
|
|
1271
1653
|
const tsConfig = parseConfig(this.cwd, ts);
|
|
@@ -1274,7 +1656,8 @@ var TestRunner = class {
|
|
|
1274
1656
|
return;
|
|
1275
1657
|
}
|
|
1276
1658
|
this.#stickyPort = String(await getPort(this.cwd));
|
|
1277
|
-
this.#
|
|
1659
|
+
this.#indexGenerator = new IndexGenerator(this.cwdPath, this.ui.logger);
|
|
1660
|
+
this.#fileSystem = new FileSystem(this.cwdPath, tsConfig, {
|
|
1278
1661
|
...this.options,
|
|
1279
1662
|
suites: this.options.suites?.filter((suite) => {
|
|
1280
1663
|
if (this.options.filters.suites) {
|
|
@@ -1283,7 +1666,10 @@ var TestRunner = class {
|
|
|
1283
1666
|
return true;
|
|
1284
1667
|
})
|
|
1285
1668
|
});
|
|
1669
|
+
this.#clearScreen();
|
|
1670
|
+
this.ui.logger.info("loading hooks...");
|
|
1286
1671
|
this.#hooks = await loadHooks(this.options.hooks, [
|
|
1672
|
+
"init",
|
|
1287
1673
|
"testsStarting",
|
|
1288
1674
|
"testsFinished",
|
|
1289
1675
|
"fileAdded",
|
|
@@ -1291,12 +1677,15 @@ var TestRunner = class {
|
|
|
1291
1677
|
"fileRemoved"
|
|
1292
1678
|
]);
|
|
1293
1679
|
this.#registerServerRestartHooks();
|
|
1294
|
-
this.#
|
|
1680
|
+
await this.#hooks.runner("init").run(this, this.#indexGenerator);
|
|
1681
|
+
this.#hooks.clear("init");
|
|
1682
|
+
this.ui.logger.info("generating indexes...");
|
|
1683
|
+
await this.#indexGenerator.generate();
|
|
1295
1684
|
this.ui.logger.info("booting application to run tests...");
|
|
1296
1685
|
await this.#runTests(this.#stickyPort);
|
|
1297
1686
|
this.#watcher = watch({
|
|
1298
1687
|
usePolling: options?.poll ?? false,
|
|
1299
|
-
cwd:
|
|
1688
|
+
cwd: this.cwdPath,
|
|
1300
1689
|
ignoreInitial: true,
|
|
1301
1690
|
ignored: (file, stats) => {
|
|
1302
1691
|
if (!stats) {
|
|
@@ -1317,30 +1706,36 @@ var TestRunner = class {
|
|
|
1317
1706
|
this.#onError?.(error);
|
|
1318
1707
|
this.#watcher?.close();
|
|
1319
1708
|
});
|
|
1320
|
-
this.#watcher.on(
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1709
|
+
this.#watcher.on("add", (filePath) => {
|
|
1710
|
+
const relativePath = string4.toUnixSlash(filePath);
|
|
1711
|
+
const absolutePath = join3(this.cwdPath, filePath);
|
|
1712
|
+
this.#hooks.runner("fileAdded").run(relativePath, absolutePath, this);
|
|
1713
|
+
});
|
|
1714
|
+
this.#watcher.on("change", (filePath) => {
|
|
1715
|
+
const relativePath = string4.toUnixSlash(filePath);
|
|
1716
|
+
const absolutePath = join3(this.cwdPath, filePath);
|
|
1717
|
+
this.#hooks.runner("fileChanged").run(
|
|
1718
|
+
relativePath,
|
|
1719
|
+
absolutePath,
|
|
1328
1720
|
{
|
|
1329
1721
|
source: "watcher",
|
|
1330
1722
|
fullReload: true,
|
|
1331
1723
|
hotReloaded: false
|
|
1332
1724
|
},
|
|
1333
1725
|
this
|
|
1334
|
-
)
|
|
1335
|
-
);
|
|
1336
|
-
this.#watcher.on(
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1726
|
+
);
|
|
1727
|
+
});
|
|
1728
|
+
this.#watcher.on("unlink", (filePath) => {
|
|
1729
|
+
const relativePath = string4.toUnixSlash(filePath);
|
|
1730
|
+
const absolutePath = join3(this.cwdPath, filePath);
|
|
1731
|
+
this.#hooks.runner("fileRemoved").run(relativePath, absolutePath, this);
|
|
1732
|
+
});
|
|
1340
1733
|
}
|
|
1341
1734
|
};
|
|
1342
1735
|
export {
|
|
1343
1736
|
Bundler,
|
|
1344
1737
|
DevServer,
|
|
1345
|
-
|
|
1738
|
+
SUPPORTED_PACKAGE_MANAGERS,
|
|
1739
|
+
TestRunner,
|
|
1740
|
+
hooks
|
|
1346
1741
|
};
|