@adonisjs/assembler 7.8.1 → 8.0.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -23
- package/build/chunk-RR4HCA4M.js +7 -0
- package/build/index.d.ts +3 -3
- package/build/index.js +688 -666
- package/build/src/bundler.d.ts +28 -7
- package/build/src/code_transformer/main.d.ts +30 -2
- package/build/src/code_transformer/main.js +80 -14
- package/build/src/code_transformer/rc_file_transformer.d.ts +7 -5
- package/build/src/dev_server.d.ts +42 -14
- package/build/src/file_system.d.ts +60 -0
- package/build/src/test_runner.d.ts +30 -6
- package/build/src/types/code_transformer.d.ts +61 -0
- package/build/src/types/common.d.ts +149 -0
- package/build/src/types/hooks.d.ts +65 -0
- package/build/src/types/main.d.ts +3 -0
- package/build/src/types/main.js +0 -0
- package/build/src/{helpers.d.ts → utils.d.ts} +28 -10
- package/package.json +37 -46
- package/build/index.js.map +0 -1
- package/build/src/assets_dev_server.d.ts +0 -31
- package/build/src/code_transformer/main.js.map +0 -1
- package/build/src/hooks.d.ts +0 -29
- package/build/src/types.d.ts +0 -245
- package/build/src/types.js +0 -1
- package/build/src/types.js.map +0 -1
package/build/index.js
CHANGED
|
@@ -1,124 +1,57 @@
|
|
|
1
|
+
import {
|
|
2
|
+
debug_default
|
|
3
|
+
} from "./chunk-RR4HCA4M.js";
|
|
4
|
+
|
|
1
5
|
// src/bundler.ts
|
|
2
|
-
import slash from "slash";
|
|
3
6
|
import dedent from "dedent";
|
|
4
|
-
import fs from "
|
|
5
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
6
|
-
import { join as join2, relative as relative2 } from "node:path";
|
|
7
|
+
import fs from "fs/promises";
|
|
7
8
|
import { cliui } from "@poppinss/cliui";
|
|
9
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
10
|
+
import { join as join2, relative as relative2 } from "path";
|
|
11
|
+
import string from "@poppinss/utils/string";
|
|
8
12
|
import { detectPackageManager } from "@antfu/install-pkg";
|
|
9
13
|
|
|
10
|
-
// src/
|
|
11
|
-
import
|
|
12
|
-
import Hooks from "@poppinss/hooks";
|
|
13
|
-
var AssemblerHooks = class {
|
|
14
|
-
#config;
|
|
15
|
-
#hooks = new Hooks();
|
|
16
|
-
constructor(config) {
|
|
17
|
-
this.#config = config;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Resolve the hook by importing the file and returning the default export
|
|
21
|
-
*/
|
|
22
|
-
async #resolveHookNode(node) {
|
|
23
|
-
const exports = await node();
|
|
24
|
-
if (!exports.default) {
|
|
25
|
-
throw new RuntimeException("Assembler hook must be defined using the default export");
|
|
26
|
-
}
|
|
27
|
-
return exports.default;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Resolve hooks needed for dev-time and register them to the Hooks instance
|
|
31
|
-
*/
|
|
32
|
-
async registerDevServerHooks() {
|
|
33
|
-
await Promise.all([
|
|
34
|
-
...(this.#config?.onDevServerStarted || []).map(
|
|
35
|
-
async (node) => this.#hooks.add("onDevServerStarted", await this.#resolveHookNode(node))
|
|
36
|
-
),
|
|
37
|
-
...(this.#config?.onSourceFileChanged || []).map(
|
|
38
|
-
async (node) => this.#hooks.add("onSourceFileChanged", await this.#resolveHookNode(node))
|
|
39
|
-
)
|
|
40
|
-
]);
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Resolve hooks needed for build-time and register them to the Hooks instance
|
|
44
|
-
*/
|
|
45
|
-
async registerBuildHooks() {
|
|
46
|
-
await Promise.all([
|
|
47
|
-
...(this.#config?.onBuildStarting || []).map(
|
|
48
|
-
async (node) => this.#hooks.add("onBuildStarting", await this.#resolveHookNode(node))
|
|
49
|
-
),
|
|
50
|
-
...(this.#config?.onBuildCompleted || []).map(
|
|
51
|
-
async (node) => this.#hooks.add("onBuildCompleted", await this.#resolveHookNode(node))
|
|
52
|
-
)
|
|
53
|
-
]);
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* When the dev server is started
|
|
57
|
-
*/
|
|
58
|
-
async onDevServerStarted(...args) {
|
|
59
|
-
await this.#hooks.runner("onDevServerStarted").run(...args);
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* When a source file changes
|
|
63
|
-
*/
|
|
64
|
-
async onSourceFileChanged(...args) {
|
|
65
|
-
await this.#hooks.runner("onSourceFileChanged").run(...args);
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* When the build process is starting
|
|
69
|
-
*/
|
|
70
|
-
async onBuildStarting(...args) {
|
|
71
|
-
await this.#hooks.runner("onBuildStarting").run(...args);
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* When the build process is completed
|
|
75
|
-
*/
|
|
76
|
-
async onBuildCompleted(...args) {
|
|
77
|
-
await this.#hooks.runner("onBuildCompleted").run(...args);
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
// src/helpers.ts
|
|
14
|
+
// src/utils.ts
|
|
15
|
+
import Cache from "tmp-cache";
|
|
82
16
|
import { isJunk } from "junk";
|
|
83
17
|
import fastGlob from "fast-glob";
|
|
18
|
+
import Hooks from "@poppinss/hooks";
|
|
19
|
+
import { existsSync } from "fs";
|
|
84
20
|
import getRandomPort from "get-port";
|
|
85
|
-
import {
|
|
86
|
-
import { fileURLToPath } from "node:url";
|
|
21
|
+
import { fileURLToPath } from "url";
|
|
87
22
|
import { execaNode, execa } from "execa";
|
|
88
|
-
import {
|
|
23
|
+
import { importDefault } from "@poppinss/utils";
|
|
24
|
+
import { copyFile, mkdir } from "fs/promises";
|
|
89
25
|
import { EnvLoader, EnvParser } from "@adonisjs/env";
|
|
90
|
-
import
|
|
91
|
-
import { basename, dirname, isAbsolute, join, relative } from "
|
|
92
|
-
|
|
93
|
-
// src/debug.ts
|
|
94
|
-
import { debuglog } from "node:util";
|
|
95
|
-
var debug_default = debuglog("adonisjs:assembler");
|
|
96
|
-
|
|
97
|
-
// src/helpers.ts
|
|
98
|
-
var DEFAULT_NODE_ARGS = [
|
|
99
|
-
// Use ts-node/esm loader. The project must install it
|
|
100
|
-
process.versions.tsNodeMaintained ? "--import=ts-node-maintained/register/esm" : "--loader=ts-node/esm",
|
|
101
|
-
// Enable source maps, since TSNode source maps are broken
|
|
102
|
-
"--enable-source-maps"
|
|
103
|
-
];
|
|
104
|
-
if (process.allowedNodeEnvironmentFlags.has("--disable-warning")) {
|
|
105
|
-
DEFAULT_NODE_ARGS.push("--disable-warning=ExperimentalWarning");
|
|
106
|
-
} else {
|
|
107
|
-
DEFAULT_NODE_ARGS.push("--no-warnings");
|
|
108
|
-
}
|
|
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"];
|
|
109
29
|
function parseConfig(cwd, ts) {
|
|
110
|
-
const
|
|
111
|
-
|
|
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) {
|
|
112
45
|
const compilerHost = ts.createCompilerHost({});
|
|
113
|
-
console.log(ts.formatDiagnosticsWithColorAndContext([
|
|
46
|
+
console.log(ts.formatDiagnosticsWithColorAndContext([hardException], compilerHost));
|
|
114
47
|
return;
|
|
115
48
|
}
|
|
116
|
-
if (
|
|
49
|
+
if (parsedConfig.errors.length) {
|
|
117
50
|
const compilerHost = ts.createCompilerHost({});
|
|
118
|
-
console.log(ts.formatDiagnosticsWithColorAndContext(
|
|
51
|
+
console.log(ts.formatDiagnosticsWithColorAndContext(parsedConfig.errors, compilerHost));
|
|
119
52
|
return;
|
|
120
53
|
}
|
|
121
|
-
return
|
|
54
|
+
return parsedConfig;
|
|
122
55
|
}
|
|
123
56
|
function runNode(cwd, options) {
|
|
124
57
|
const childProcess = execaNode(options.script, options.scriptArgs, {
|
|
@@ -152,20 +85,8 @@ function run(cwd, options) {
|
|
|
152
85
|
});
|
|
153
86
|
return childProcess;
|
|
154
87
|
}
|
|
155
|
-
function watch(
|
|
156
|
-
|
|
157
|
-
if (!config) {
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
const watcher = new Watcher(typeof cwd === "string" ? cwd : fileURLToPath(cwd), config);
|
|
161
|
-
const chokidar = watcher.watch(["."], { usePolling: options.poll });
|
|
162
|
-
return { watcher, chokidar };
|
|
163
|
-
}
|
|
164
|
-
function isDotEnvFile(filePath) {
|
|
165
|
-
if (filePath === ".env") {
|
|
166
|
-
return true;
|
|
167
|
-
}
|
|
168
|
-
return filePath.includes(".env.");
|
|
88
|
+
function watch(options) {
|
|
89
|
+
return chokidar.watch(["."], options);
|
|
169
90
|
}
|
|
170
91
|
async function getPort(cwd) {
|
|
171
92
|
if (process.env.PORT) {
|
|
@@ -207,9 +128,57 @@ async function copyFiles(files, cwd, outDir) {
|
|
|
207
128
|
});
|
|
208
129
|
return await Promise.all(copyPromises);
|
|
209
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
|
+
}
|
|
210
179
|
|
|
211
180
|
// src/bundler.ts
|
|
212
|
-
var
|
|
181
|
+
var SUPPORTED_PACKAGE_MANAGERS = {
|
|
213
182
|
"npm": {
|
|
214
183
|
packageManagerFiles: ["package-lock.json"],
|
|
215
184
|
installCommand: 'npm ci --omit="dev"'
|
|
@@ -231,33 +200,26 @@ var SUPPORT_PACKAGE_MANAGERS = {
|
|
|
231
200
|
installCommand: "bun install --production"
|
|
232
201
|
}
|
|
233
202
|
};
|
|
234
|
-
var ui = cliui();
|
|
235
203
|
var Bundler = class {
|
|
236
|
-
|
|
204
|
+
constructor(cwd, ts, options) {
|
|
205
|
+
this.cwd = cwd;
|
|
206
|
+
this.options = options;
|
|
207
|
+
this.#cwdPath = fileURLToPath2(this.cwd);
|
|
208
|
+
this.#ts = ts;
|
|
209
|
+
}
|
|
237
210
|
#cwdPath;
|
|
238
211
|
#ts;
|
|
239
|
-
#logger = ui.logger;
|
|
240
|
-
#hooks;
|
|
241
|
-
#options;
|
|
242
212
|
/**
|
|
243
|
-
*
|
|
213
|
+
* Hooks to execute custom actions during the build process
|
|
244
214
|
*/
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
constructor(cwd, ts, options) {
|
|
249
|
-
this.#cwd = cwd;
|
|
250
|
-
this.#cwdPath = fileURLToPath2(this.#cwd);
|
|
251
|
-
this.#ts = ts;
|
|
252
|
-
this.#options = options;
|
|
253
|
-
this.#hooks = new AssemblerHooks(options.hooks);
|
|
254
|
-
}
|
|
215
|
+
#hooks;
|
|
216
|
+
ui = cliui();
|
|
255
217
|
/**
|
|
256
218
|
* Returns the relative unix path for an absolute
|
|
257
219
|
* file path
|
|
258
220
|
*/
|
|
259
221
|
#getRelativeName(filePath) {
|
|
260
|
-
return
|
|
222
|
+
return string.toUnixSlash(relative2(this.#cwdPath, filePath));
|
|
261
223
|
}
|
|
262
224
|
/**
|
|
263
225
|
* Cleans up the build directory
|
|
@@ -265,32 +227,12 @@ var Bundler = class {
|
|
|
265
227
|
async #cleanupBuildDirectory(outDir) {
|
|
266
228
|
await fs.rm(outDir, { recursive: true, force: true, maxRetries: 5 });
|
|
267
229
|
}
|
|
268
|
-
/**
|
|
269
|
-
* Runs assets bundler command to build assets
|
|
270
|
-
*/
|
|
271
|
-
async #buildAssets() {
|
|
272
|
-
const assetsBundler = this.#options.assets;
|
|
273
|
-
if (!assetsBundler?.enabled) {
|
|
274
|
-
return true;
|
|
275
|
-
}
|
|
276
|
-
try {
|
|
277
|
-
this.#logger.info("compiling frontend assets", { suffix: assetsBundler.cmd });
|
|
278
|
-
await run(this.#cwd, {
|
|
279
|
-
stdio: "inherit",
|
|
280
|
-
script: assetsBundler.cmd,
|
|
281
|
-
scriptArgs: assetsBundler.args
|
|
282
|
-
});
|
|
283
|
-
return true;
|
|
284
|
-
} catch {
|
|
285
|
-
return false;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
230
|
/**
|
|
289
231
|
* Runs tsc command to build the source.
|
|
290
232
|
*/
|
|
291
233
|
async #runTsc(outDir) {
|
|
292
234
|
try {
|
|
293
|
-
await run(this
|
|
235
|
+
await run(this.cwd, {
|
|
294
236
|
stdio: "inherit",
|
|
295
237
|
script: "tsc",
|
|
296
238
|
scriptArgs: ["--outDir", outDir]
|
|
@@ -304,26 +246,21 @@ var Bundler = class {
|
|
|
304
246
|
* Copy meta files to the output directory
|
|
305
247
|
*/
|
|
306
248
|
async #copyMetaFiles(outDir, additionalFilesToCopy) {
|
|
307
|
-
const metaFiles = (this
|
|
249
|
+
const metaFiles = (this.options.metaFiles || []).map((file) => file.pattern).concat(additionalFilesToCopy);
|
|
308
250
|
await copyFiles(metaFiles, this.#cwdPath, outDir);
|
|
309
251
|
}
|
|
310
252
|
/**
|
|
311
253
|
* Detect the package manager used by the project
|
|
312
|
-
* and return the lockfile name and install command
|
|
313
|
-
* related to it.
|
|
314
254
|
*/
|
|
315
|
-
async #
|
|
316
|
-
|
|
317
|
-
if (
|
|
318
|
-
|
|
255
|
+
async #detectPackageManager() {
|
|
256
|
+
const pkgManager = await detectPackageManager(this.#cwdPath);
|
|
257
|
+
if (pkgManager === "deno") {
|
|
258
|
+
return "npm";
|
|
319
259
|
}
|
|
320
|
-
if (
|
|
321
|
-
|
|
260
|
+
if (pkgManager === "pnpm@6") {
|
|
261
|
+
return "pnpm";
|
|
322
262
|
}
|
|
323
|
-
|
|
324
|
-
return null;
|
|
325
|
-
}
|
|
326
|
-
return SUPPORT_PACKAGE_MANAGERS[pkgManager];
|
|
263
|
+
return pkgManager;
|
|
327
264
|
}
|
|
328
265
|
/**
|
|
329
266
|
* Rewrite the ace file since the original one
|
|
@@ -345,257 +282,346 @@ var Bundler = class {
|
|
|
345
282
|
`
|
|
346
283
|
);
|
|
347
284
|
await fs.writeFile(aceFileLocation, aceFileContent);
|
|
348
|
-
this
|
|
349
|
-
}
|
|
350
|
-
/**
|
|
351
|
-
* Set a custom CLI UI logger
|
|
352
|
-
*/
|
|
353
|
-
setLogger(logger) {
|
|
354
|
-
this.#logger = logger;
|
|
355
|
-
return this;
|
|
285
|
+
this.ui.logger.info("created ace file", { suffix: this.#getRelativeName(aceFileLocation) });
|
|
356
286
|
}
|
|
357
287
|
/**
|
|
358
288
|
* Bundles the application to be run in production
|
|
359
289
|
*/
|
|
360
290
|
async bundle(stopOnError = true, client) {
|
|
361
|
-
await this
|
|
362
|
-
|
|
291
|
+
this.#hooks = await loadHooks(this.options.hooks, ["buildStarting", "buildFinished"]);
|
|
292
|
+
this.packageManager = client ?? await this.#detectPackageManager() ?? "npm";
|
|
293
|
+
const config = parseConfig(this.cwd, this.#ts);
|
|
363
294
|
if (!config) {
|
|
364
295
|
return false;
|
|
365
296
|
}
|
|
366
|
-
const outDir = config.options.outDir || fileURLToPath2(new URL("build/", this
|
|
367
|
-
this
|
|
297
|
+
const outDir = config.options.outDir || fileURLToPath2(new URL("build/", this.cwd));
|
|
298
|
+
this.ui.logger.info("cleaning up output directory", { suffix: this.#getRelativeName(outDir) });
|
|
368
299
|
await this.#cleanupBuildDirectory(outDir);
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
}
|
|
372
|
-
await this.#hooks.onBuildStarting({ colors: ui.colors, logger: this.#logger });
|
|
373
|
-
this.#logger.info("compiling typescript source", { suffix: "tsc" });
|
|
300
|
+
await this.#hooks.runner("buildStarting").run(this);
|
|
301
|
+
this.ui.logger.info("compiling typescript source", { suffix: "tsc" });
|
|
374
302
|
const buildCompleted = await this.#runTsc(outDir);
|
|
375
303
|
await this.#createAceFile(outDir);
|
|
376
304
|
if (!buildCompleted && stopOnError) {
|
|
377
305
|
await this.#cleanupBuildDirectory(outDir);
|
|
378
|
-
const instructions = ui.sticker().fullScreen().drawBorder((borderChar, colors) => colors.red(borderChar));
|
|
306
|
+
const instructions = this.ui.sticker().fullScreen().drawBorder((borderChar, colors) => colors.red(borderChar));
|
|
379
307
|
instructions.add(
|
|
380
|
-
this
|
|
308
|
+
this.ui.colors.red("Cannot complete the build process as there are TypeScript errors.")
|
|
381
309
|
);
|
|
382
310
|
instructions.add(
|
|
383
|
-
this
|
|
311
|
+
this.ui.colors.red(
|
|
384
312
|
'Use "--ignore-ts-errors" flag to ignore TypeScript errors and continue the build.'
|
|
385
313
|
)
|
|
386
314
|
);
|
|
387
|
-
this
|
|
315
|
+
this.ui.logger.logError(instructions.prepare());
|
|
388
316
|
return false;
|
|
389
317
|
}
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
|
|
318
|
+
const pkgFiles = [
|
|
319
|
+
"package.json",
|
|
320
|
+
...SUPPORTED_PACKAGE_MANAGERS[this.packageManager].packageManagerFiles
|
|
321
|
+
];
|
|
322
|
+
this.ui.logger.info("copying meta files to the output directory");
|
|
393
323
|
await this.#copyMetaFiles(outDir, pkgFiles);
|
|
394
|
-
this
|
|
395
|
-
this
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
pkgManager ? pkgManager.installCommand : "Install production dependencies"
|
|
400
|
-
)
|
|
401
|
-
).add(this.#colors.cyan("node bin/server.js")).render();
|
|
324
|
+
this.ui.logger.success("build completed");
|
|
325
|
+
this.ui.logger.log("");
|
|
326
|
+
const displayMessage = this.ui.instructions().heading("Run the following commands to start the server in production");
|
|
327
|
+
await this.#hooks.runner("buildFinished").run(this, displayMessage);
|
|
328
|
+
displayMessage.add(this.ui.colors.cyan(`cd ${this.#getRelativeName(outDir)}`)).add(this.ui.colors.cyan(SUPPORTED_PACKAGE_MANAGERS[this.packageManager].installCommand)).add(this.ui.colors.cyan("node bin/server.js")).render();
|
|
402
329
|
return true;
|
|
403
330
|
}
|
|
404
331
|
};
|
|
405
332
|
|
|
406
333
|
// src/dev_server.ts
|
|
407
|
-
import
|
|
408
|
-
import { relative as relative3 } from "node:path";
|
|
334
|
+
import { cliui as cliui2 } from "@poppinss/cliui";
|
|
409
335
|
import prettyHrtime from "pretty-hrtime";
|
|
410
|
-
import { fileURLToPath as
|
|
411
|
-
import
|
|
336
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
337
|
+
import string3 from "@poppinss/utils/string";
|
|
412
338
|
|
|
413
|
-
// src/
|
|
414
|
-
import
|
|
415
|
-
|
|
416
|
-
|
|
339
|
+
// src/file_system.ts
|
|
340
|
+
import picomatch from "picomatch";
|
|
341
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
342
|
+
import { join as join3, relative as relative3 } from "path";
|
|
343
|
+
import string2 from "@poppinss/utils/string";
|
|
344
|
+
var DEFAULT_INCLUDES = ["**/*"];
|
|
345
|
+
var ALWAYS_EXCLUDE = [".git/**", "coverage/**", ".github/**"];
|
|
346
|
+
var DEFAULT_EXCLUDES = ["node_modules/**", "bower_components/**", "jspm_packages/**"];
|
|
347
|
+
var FileSystem = class {
|
|
348
|
+
/**
|
|
349
|
+
* The current working project directory
|
|
350
|
+
*/
|
|
417
351
|
#cwd;
|
|
418
|
-
#logger = ui2.logger;
|
|
419
|
-
#options;
|
|
420
|
-
#devServer;
|
|
421
352
|
/**
|
|
422
|
-
*
|
|
353
|
+
* Referenced to the parsed ts config file. We use it to read the includes,
|
|
354
|
+
* excludes and pre-scanned files.
|
|
423
355
|
*/
|
|
424
|
-
|
|
425
|
-
return this.#logger.getColors();
|
|
426
|
-
}
|
|
427
|
-
constructor(cwd, options) {
|
|
428
|
-
this.#cwd = cwd;
|
|
429
|
-
this.#options = options;
|
|
430
|
-
}
|
|
356
|
+
#tsConfig;
|
|
431
357
|
/**
|
|
432
|
-
*
|
|
358
|
+
* Set of pre-scanned typeScript files provided by tsconfig
|
|
433
359
|
*/
|
|
434
|
-
#
|
|
435
|
-
const dataString = data.toString();
|
|
436
|
-
const lines = dataString.split("\n");
|
|
437
|
-
if (dataString.includes("Local") && dataString.includes("Network")) {
|
|
438
|
-
const sticker = ui2.sticker().useColors(this.#colors).useRenderer(this.#logger.getRenderer());
|
|
439
|
-
lines.forEach((line) => {
|
|
440
|
-
if (line.trim()) {
|
|
441
|
-
sticker.add(line);
|
|
442
|
-
}
|
|
443
|
-
});
|
|
444
|
-
sticker.render();
|
|
445
|
-
return;
|
|
446
|
-
}
|
|
447
|
-
if (dataString.includes("ready in")) {
|
|
448
|
-
console.log("");
|
|
449
|
-
console.log(dataString.trim());
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
lines.forEach((line) => {
|
|
453
|
-
if (line.trim()) {
|
|
454
|
-
console.log(line);
|
|
455
|
-
}
|
|
456
|
-
});
|
|
457
|
-
}
|
|
360
|
+
#scannedTypeScriptFiles = /* @__PURE__ */ new Set();
|
|
458
361
|
/**
|
|
459
|
-
*
|
|
362
|
+
* Picomatch matcher function to know if a file path is
|
|
363
|
+
* part of includes
|
|
460
364
|
*/
|
|
461
|
-
#
|
|
462
|
-
const dataString = data.toString();
|
|
463
|
-
const lines = dataString.split("\n");
|
|
464
|
-
lines.forEach((line) => {
|
|
465
|
-
if (line.trim()) {
|
|
466
|
-
console.log(line);
|
|
467
|
-
}
|
|
468
|
-
});
|
|
469
|
-
}
|
|
365
|
+
#isIncluded;
|
|
470
366
|
/**
|
|
471
|
-
*
|
|
367
|
+
* Picomatch matcher function to know if a file path is
|
|
368
|
+
* part of excludes
|
|
472
369
|
*/
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
370
|
+
#isExcluded;
|
|
371
|
+
/**
|
|
372
|
+
* Picomatch matcher function to know if a file path is a
|
|
373
|
+
* meta file with reloadServer option enabled
|
|
374
|
+
*/
|
|
375
|
+
#isMetaFileWithReloadsEnabled;
|
|
376
|
+
/**
|
|
377
|
+
* Picomatch matcher function to know if a file path is a
|
|
378
|
+
* meta file with reloadServer option disabled
|
|
379
|
+
*/
|
|
380
|
+
#isMetaFileWithReloadsDisabled;
|
|
381
|
+
/**
|
|
382
|
+
* Picomatch matcher function to know if a file path is a
|
|
383
|
+
* test file or not
|
|
384
|
+
*/
|
|
385
|
+
#isTestFile;
|
|
386
|
+
/**
|
|
387
|
+
* References to includes and excludes
|
|
388
|
+
*/
|
|
389
|
+
#includes;
|
|
390
|
+
#excludes;
|
|
391
|
+
/**
|
|
392
|
+
* Includes glob patterns extracted from "tsconfig.json" file.
|
|
393
|
+
* Defaults to: ["**\/*"]
|
|
394
|
+
*/
|
|
395
|
+
get includes() {
|
|
396
|
+
return this.#includes;
|
|
476
397
|
}
|
|
477
398
|
/**
|
|
478
|
-
*
|
|
479
|
-
*
|
|
480
|
-
*
|
|
399
|
+
* Excludes glob patterns extracted from "tsconfig.json" file.
|
|
400
|
+
*
|
|
401
|
+
* Defaults to: [
|
|
402
|
+
* 'node_modules/**',
|
|
403
|
+
* 'bower_components/**',
|
|
404
|
+
* 'jspm_packages/**,
|
|
405
|
+
* ]
|
|
406
|
+
*
|
|
407
|
+
* Following patterns are always ignored
|
|
408
|
+
*
|
|
409
|
+
* '.git/**', 'coverage/**', '.github/**'
|
|
410
|
+
*/
|
|
411
|
+
get excludes() {
|
|
412
|
+
return this.#excludes;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Inspect a relative path to find its source in the project
|
|
416
|
+
*/
|
|
417
|
+
inspect = memoize((filePath) => {
|
|
418
|
+
const relativePath = filePath;
|
|
419
|
+
const absolutePath = string2.toUnixSlash(join3(this.#cwd, relativePath));
|
|
420
|
+
if (this.#isScriptFile(relativePath) && (this.#scannedTypeScriptFiles.has(absolutePath) || this.#isPartOfBackendProject(relativePath))) {
|
|
421
|
+
debug_default('backend project file "%s"', relativePath);
|
|
422
|
+
const isTestFile = this.#isTestFile(relativePath);
|
|
423
|
+
return {
|
|
424
|
+
fileType: isTestFile ? "test" : "script",
|
|
425
|
+
reloadServer: !isTestFile,
|
|
426
|
+
unixRelativePath: relativePath,
|
|
427
|
+
unixAbsolutePath: absolutePath
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
if (this.#isMetaFileWithReloadsEnabled(relativePath)) {
|
|
431
|
+
debug_default('meta file "%s"', relativePath);
|
|
432
|
+
return {
|
|
433
|
+
fileType: "meta",
|
|
434
|
+
reloadServer: true,
|
|
435
|
+
unixRelativePath: relativePath,
|
|
436
|
+
unixAbsolutePath: absolutePath
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
if (this.#isMetaFileWithReloadsDisabled(relativePath)) {
|
|
440
|
+
debug_default('meta file "%s"', relativePath);
|
|
441
|
+
return {
|
|
442
|
+
fileType: "meta",
|
|
443
|
+
reloadServer: false,
|
|
444
|
+
unixRelativePath: relativePath,
|
|
445
|
+
unixAbsolutePath: absolutePath
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
debug_default('ignored file "%s"', relativePath);
|
|
449
|
+
return null;
|
|
450
|
+
});
|
|
451
|
+
/**
|
|
452
|
+
* Returns true if the directory should be watched. Chokidar sends
|
|
453
|
+
* absolute unix paths to the ignored callback.
|
|
454
|
+
*
|
|
455
|
+
* You must use "shouldWatchFile" method for files and call this method
|
|
456
|
+
* for directories only.
|
|
481
457
|
*/
|
|
482
|
-
|
|
483
|
-
if (
|
|
484
|
-
|
|
458
|
+
shouldWatchDirectory = memoize((absolutePath) => {
|
|
459
|
+
if (absolutePath === this.#cwd) {
|
|
460
|
+
debug_default("watching project root");
|
|
461
|
+
return true;
|
|
485
462
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
463
|
+
const relativePath = string2.toUnixSlash(relative3(this.#cwd, absolutePath));
|
|
464
|
+
if (this.#isExcluded(relativePath)) {
|
|
465
|
+
debug_default('watching "%s"', absolutePath);
|
|
466
|
+
return false;
|
|
467
|
+
}
|
|
468
|
+
return true;
|
|
469
|
+
});
|
|
470
|
+
constructor(cwd, tsConfig, rcFile) {
|
|
471
|
+
this.#cwd = string2.toUnixSlash(typeof cwd === "string" ? cwd : fileURLToPath3(cwd));
|
|
472
|
+
this.#tsConfig = tsConfig;
|
|
473
|
+
const files = tsConfig.fileNames;
|
|
474
|
+
const metaFiles = rcFile.metaFiles ?? [];
|
|
475
|
+
const testSuites = rcFile.suites ?? [];
|
|
476
|
+
const outDir = tsConfig.raw.compilerOptions?.outDir;
|
|
477
|
+
files.forEach((file) => this.#scannedTypeScriptFiles.add(string2.toUnixSlash(file)));
|
|
478
|
+
this.#includes = tsConfig.raw.include || DEFAULT_INCLUDES;
|
|
479
|
+
this.#excludes = ALWAYS_EXCLUDE.concat(
|
|
480
|
+
tsConfig.raw.exclude || (outDir ? DEFAULT_EXCLUDES.concat(outDir) : DEFAULT_EXCLUDES)
|
|
481
|
+
);
|
|
482
|
+
this.#isMetaFileWithReloadsEnabled = picomatch(
|
|
483
|
+
metaFiles.filter((file) => !!file.reloadServer).map((file) => file.pattern),
|
|
484
|
+
{
|
|
485
|
+
cwd: this.#cwd
|
|
502
486
|
}
|
|
503
|
-
|
|
504
|
-
this.#
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
this.#logAssetsDevServerMessage(data);
|
|
487
|
+
);
|
|
488
|
+
this.#isMetaFileWithReloadsDisabled = picomatch(
|
|
489
|
+
metaFiles.filter((file) => !file.reloadServer).map((file) => file.pattern),
|
|
490
|
+
{
|
|
491
|
+
cwd: this.#cwd
|
|
509
492
|
}
|
|
493
|
+
);
|
|
494
|
+
this.#isTestFile = picomatch(
|
|
495
|
+
testSuites.flatMap((suite) => suite.files),
|
|
496
|
+
{
|
|
497
|
+
cwd: this.#cwd
|
|
498
|
+
}
|
|
499
|
+
);
|
|
500
|
+
this.#isIncluded = picomatch(this.#includes, {
|
|
501
|
+
cwd: this.#cwd
|
|
510
502
|
});
|
|
511
|
-
this.#
|
|
512
|
-
this.#
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
503
|
+
this.#isExcluded = picomatch(this.#excludes, {
|
|
504
|
+
cwd: this.#cwd
|
|
505
|
+
});
|
|
506
|
+
debug_default("initiating file system %O", {
|
|
507
|
+
includes: this.#includes,
|
|
508
|
+
excludes: this.#includes,
|
|
509
|
+
outDir,
|
|
510
|
+
files,
|
|
511
|
+
metaFiles,
|
|
512
|
+
testSuites
|
|
518
513
|
});
|
|
519
514
|
}
|
|
520
515
|
/**
|
|
521
|
-
*
|
|
516
|
+
* Returns a boolean telling if a file path is a script file or not.
|
|
517
|
+
*
|
|
518
|
+
* - Files ending with ".ts", ".tsx" are considered are script files.
|
|
519
|
+
* - Files ending with ".js" with "allowJs" option enabled are considered are script files.
|
|
520
|
+
* - Files ending with ".json" with "resolveJsonModule" option enabled are considered are script files.
|
|
522
521
|
*/
|
|
523
|
-
|
|
524
|
-
if (
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
522
|
+
#isScriptFile(relativePath) {
|
|
523
|
+
if ((relativePath.endsWith(".ts") || relativePath.endsWith(".tsx")) && !relativePath.endsWith(".d.ts")) {
|
|
524
|
+
return true;
|
|
525
|
+
}
|
|
526
|
+
if (this.#tsConfig.options.allowJs && relativePath.endsWith(".js")) {
|
|
527
|
+
return true;
|
|
528
528
|
}
|
|
529
|
+
if (this.#tsConfig.options.resolveJsonModule && relativePath.endsWith(".json")) {
|
|
530
|
+
return true;
|
|
531
|
+
}
|
|
532
|
+
return false;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Check if the file path is part of the backend TypeScript project. We use
|
|
536
|
+
* tsconfig "includes", "excludes", and "files" paths to test if the file
|
|
537
|
+
* should be considered or not.
|
|
538
|
+
*/
|
|
539
|
+
#isPartOfBackendProject(relativePath) {
|
|
540
|
+
if (this.#isExcluded(relativePath)) {
|
|
541
|
+
debug_default('excluded by tsconfig "%s"', relativePath);
|
|
542
|
+
return false;
|
|
543
|
+
}
|
|
544
|
+
if (this.#isIncluded(relativePath)) {
|
|
545
|
+
debug_default('included by tsconfig "%s"', relativePath);
|
|
546
|
+
return true;
|
|
547
|
+
}
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Returns true if the file should be watched. Chokidar sends
|
|
552
|
+
* absolute unix paths to the ignored callback.
|
|
553
|
+
*
|
|
554
|
+
* You must use "shouldWatchDirectory" method for directories and call
|
|
555
|
+
* this method for files only.
|
|
556
|
+
*/
|
|
557
|
+
shouldWatchFile(absolutePath) {
|
|
558
|
+
return this.inspect(relative3(this.#cwd, absolutePath)) !== null;
|
|
529
559
|
}
|
|
530
560
|
};
|
|
531
561
|
|
|
532
562
|
// src/dev_server.ts
|
|
533
|
-
var ui3 = cliui3();
|
|
534
563
|
var DevServer = class {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
* Flag to know if the dev server is running in watch
|
|
540
|
-
* mode
|
|
541
|
-
*/
|
|
542
|
-
#isWatching = false;
|
|
564
|
+
constructor(cwd, options) {
|
|
565
|
+
this.cwd = cwd;
|
|
566
|
+
this.options = options;
|
|
567
|
+
}
|
|
543
568
|
/**
|
|
544
|
-
*
|
|
569
|
+
* External listeners that are invoked when child process
|
|
570
|
+
* gets an error or closes
|
|
545
571
|
*/
|
|
546
|
-
#
|
|
572
|
+
#onError;
|
|
573
|
+
#onClose;
|
|
547
574
|
/**
|
|
548
|
-
*
|
|
549
|
-
*
|
|
575
|
+
* The stickyPort is set by the start and the startAndWatch methods
|
|
576
|
+
* and we will continue to use that port during restart
|
|
550
577
|
*/
|
|
551
|
-
#
|
|
578
|
+
#stickyPort;
|
|
552
579
|
/**
|
|
553
|
-
*
|
|
554
|
-
* meta file with reloadServer option disabled
|
|
580
|
+
* The mode is set by the start and the startAndWatch methods
|
|
555
581
|
*/
|
|
556
|
-
#
|
|
582
|
+
#mode = "static";
|
|
557
583
|
/**
|
|
558
|
-
*
|
|
559
|
-
* gets an error or closes
|
|
584
|
+
* Reference to chokidar watcher
|
|
560
585
|
*/
|
|
561
|
-
#
|
|
562
|
-
#onClose;
|
|
586
|
+
#watcher;
|
|
563
587
|
/**
|
|
564
588
|
* Reference to the child process
|
|
565
589
|
*/
|
|
566
590
|
#httpServer;
|
|
567
591
|
/**
|
|
568
|
-
*
|
|
592
|
+
* Filesystem is used to decide which files to watch or entertain when
|
|
593
|
+
* using hot-hook
|
|
569
594
|
*/
|
|
570
|
-
#
|
|
571
|
-
/**
|
|
572
|
-
* Reference to the assets server
|
|
573
|
-
*/
|
|
574
|
-
#assetsServer;
|
|
595
|
+
#fileSystem;
|
|
575
596
|
/**
|
|
576
597
|
* Hooks to execute custom actions during the dev server lifecycle
|
|
577
598
|
*/
|
|
578
599
|
#hooks;
|
|
579
600
|
/**
|
|
580
|
-
*
|
|
601
|
+
* Restarts the HTTP server and throttle concurrent calls to
|
|
602
|
+
* ensure we do not end up with a long loop of restarts
|
|
581
603
|
*/
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
this.#cwd = cwd;
|
|
587
|
-
this.#options = options;
|
|
588
|
-
this.#hooks = new AssemblerHooks(options.hooks);
|
|
589
|
-
if (this.#options.hmr) {
|
|
590
|
-
this.#options.nodeArgs = this.#options.nodeArgs.concat(["--import=hot-hook/register"]);
|
|
604
|
+
#restartHTTPServer = throttle(async () => {
|
|
605
|
+
if (this.#httpServer) {
|
|
606
|
+
this.#httpServer.removeAllListeners();
|
|
607
|
+
this.#httpServer.kill("SIGKILL");
|
|
591
608
|
}
|
|
592
|
-
this.#
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
609
|
+
await this.#startHTTPServer(this.#stickyPort);
|
|
610
|
+
}, "restartHTTPServer");
|
|
611
|
+
/**
|
|
612
|
+
* CLI UI to log colorful messages
|
|
613
|
+
*/
|
|
614
|
+
ui = cliui2();
|
|
615
|
+
/**
|
|
616
|
+
* The mode in which the DevServer is running.
|
|
617
|
+
*/
|
|
618
|
+
get mode() {
|
|
619
|
+
return this.#mode;
|
|
598
620
|
}
|
|
621
|
+
/**
|
|
622
|
+
* Script file to start the development server
|
|
623
|
+
*/
|
|
624
|
+
scriptFile = "bin/server.ts";
|
|
599
625
|
/**
|
|
600
626
|
* Inspect if child process message is from AdonisJS HTTP server
|
|
601
627
|
*/
|
|
@@ -603,7 +629,25 @@ var DevServer = class {
|
|
|
603
629
|
return message !== null && typeof message === "object" && "isAdonisJS" in message && "environment" in message && message.environment === "web";
|
|
604
630
|
}
|
|
605
631
|
/**
|
|
606
|
-
*
|
|
632
|
+
* Displays the server info and executes the hooks after the server has been
|
|
633
|
+
* started.
|
|
634
|
+
*/
|
|
635
|
+
async #postServerReady(message) {
|
|
636
|
+
const host = message.host === "0.0.0.0" ? "127.0.0.1" : message.host;
|
|
637
|
+
const displayMessage = this.ui.sticker().add(`Server address: ${this.ui.colors.cyan(`http://${host}:${message.port}`)}`).add(`Mode: ${this.ui.colors.cyan(this.mode)}`);
|
|
638
|
+
if (message.duration) {
|
|
639
|
+
displayMessage.add(`Ready in: ${this.ui.colors.cyan(prettyHrtime(message.duration))}`);
|
|
640
|
+
}
|
|
641
|
+
try {
|
|
642
|
+
await this.#hooks.runner("devServerStarted").run(this, displayMessage);
|
|
643
|
+
} catch (error) {
|
|
644
|
+
this.ui.logger.error('One of the "devServerStarted" hooks failed');
|
|
645
|
+
this.ui.logger.fatal(error);
|
|
646
|
+
}
|
|
647
|
+
displayMessage.render();
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Inspect if child process message is coming from hot-hook
|
|
607
651
|
*/
|
|
608
652
|
#isHotHookMessage(message) {
|
|
609
653
|
return message !== null && typeof message === "object" && "type" in message && typeof message.type === "string" && message.type.startsWith("hot-hook:");
|
|
@@ -612,120 +656,96 @@ var DevServer = class {
|
|
|
612
656
|
* Conditionally clear the terminal screen
|
|
613
657
|
*/
|
|
614
658
|
#clearScreen() {
|
|
615
|
-
if (this
|
|
659
|
+
if (this.options.clearScreen) {
|
|
616
660
|
process.stdout.write("\x1Bc");
|
|
617
661
|
}
|
|
618
662
|
}
|
|
619
663
|
/**
|
|
620
|
-
*
|
|
621
|
-
*/
|
|
622
|
-
#startHTTPServer(port, mode) {
|
|
623
|
-
const hooksArgs = { colors: this.#colors, logger: this.#logger };
|
|
624
|
-
this.#httpServer = runNode(this.#cwd, {
|
|
625
|
-
script: this.#scriptFile,
|
|
626
|
-
env: { PORT: port, ...this.#options.env },
|
|
627
|
-
nodeArgs: this.#options.nodeArgs,
|
|
628
|
-
scriptArgs: this.#options.scriptArgs
|
|
629
|
-
});
|
|
630
|
-
this.#httpServer.on("message", async (message) => {
|
|
631
|
-
if (this.#isHotHookMessage(message)) {
|
|
632
|
-
const path = relative3(fileURLToPath3(this.#cwd), message.path || message.paths?.[0]);
|
|
633
|
-
this.#hooks.onSourceFileChanged(hooksArgs, path);
|
|
634
|
-
if (message.type === "hot-hook:full-reload") {
|
|
635
|
-
this.#clearScreen();
|
|
636
|
-
this.#logger.log(`${this.#colors.green("full-reload")} ${path}`);
|
|
637
|
-
this.#restartHTTPServer(port);
|
|
638
|
-
this.#hooks.onDevServerStarted(hooksArgs);
|
|
639
|
-
} else if (message.type === "hot-hook:invalidated") {
|
|
640
|
-
this.#logger.log(`${this.#colors.green("invalidated")} ${path}`);
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
if (this.#isAdonisJSReadyMessage(message)) {
|
|
644
|
-
const host = message.host === "0.0.0.0" ? "127.0.0.1" : message.host;
|
|
645
|
-
const displayMessage = ui3.sticker().useColors(this.#colors).useRenderer(this.#logger.getRenderer()).add(`Server address: ${this.#colors.cyan(`http://${host}:${message.port}`)}`);
|
|
646
|
-
const watchMode = this.#options.hmr ? "HMR" : this.#isWatching ? "Legacy" : "None";
|
|
647
|
-
displayMessage.add(`Watch Mode: ${this.#colors.cyan(watchMode)}`);
|
|
648
|
-
if (message.duration) {
|
|
649
|
-
displayMessage.add(`Ready in: ${this.#colors.cyan(prettyHrtime(message.duration))}`);
|
|
650
|
-
}
|
|
651
|
-
displayMessage.render();
|
|
652
|
-
await this.#hooks.onDevServerStarted({ colors: ui3.colors, logger: this.#logger });
|
|
653
|
-
}
|
|
654
|
-
});
|
|
655
|
-
this.#httpServer.then((result) => {
|
|
656
|
-
if (mode === "nonblocking") {
|
|
657
|
-
this.#onClose?.(result.exitCode);
|
|
658
|
-
this.#watcher?.close();
|
|
659
|
-
this.#assetsServer?.stop();
|
|
660
|
-
} else {
|
|
661
|
-
this.#logger.info("Underlying HTTP server closed. Still watching for changes");
|
|
662
|
-
}
|
|
663
|
-
}).catch((error) => {
|
|
664
|
-
if (mode === "nonblocking") {
|
|
665
|
-
this.#onError?.(error);
|
|
666
|
-
this.#watcher?.close();
|
|
667
|
-
this.#assetsServer?.stop();
|
|
668
|
-
} else {
|
|
669
|
-
this.#logger.info("Underlying HTTP server died. Still watching for changes");
|
|
670
|
-
}
|
|
671
|
-
});
|
|
672
|
-
}
|
|
673
|
-
/**
|
|
674
|
-
* Starts the assets server
|
|
675
|
-
*/
|
|
676
|
-
#startAssetsServer() {
|
|
677
|
-
this.#assetsServer = new AssetsDevServer(this.#cwd, this.#options.assets);
|
|
678
|
-
this.#assetsServer.setLogger(this.#logger);
|
|
679
|
-
this.#assetsServer.start();
|
|
680
|
-
}
|
|
681
|
-
/**
|
|
682
|
-
* Restarts the HTTP server in the watch mode. Do not call this
|
|
683
|
-
* method when not in watch mode
|
|
684
|
-
*/
|
|
685
|
-
#restartHTTPServer(port) {
|
|
686
|
-
if (this.#httpServer) {
|
|
687
|
-
this.#httpServer.removeAllListeners();
|
|
688
|
-
this.#httpServer.kill("SIGKILL");
|
|
689
|
-
}
|
|
690
|
-
this.#startHTTPServer(port, "blocking");
|
|
691
|
-
}
|
|
692
|
-
/**
|
|
693
|
-
* Handles a non TypeScript file change
|
|
664
|
+
* Handles file change event
|
|
694
665
|
*/
|
|
695
|
-
#handleFileChange(
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
this.#logger.log(`${this.#colors.green(action)} ${relativePath}`);
|
|
699
|
-
this.#restartHTTPServer(port);
|
|
666
|
+
#handleFileChange(filePath, action, hotReplaced) {
|
|
667
|
+
const file = this.#fileSystem.inspect(filePath);
|
|
668
|
+
if (!file) {
|
|
700
669
|
return;
|
|
701
670
|
}
|
|
702
|
-
if (
|
|
703
|
-
this
|
|
704
|
-
this.#logger.log(`${this.#colors.green(action)} ${relativePath}`);
|
|
705
|
-
this.#restartHTTPServer(port);
|
|
671
|
+
if (hotReplaced) {
|
|
672
|
+
this.ui.logger.log(`${this.ui.colors.green("invalidated")} ${filePath}`);
|
|
706
673
|
return;
|
|
707
674
|
}
|
|
708
|
-
if (
|
|
675
|
+
if (file.reloadServer) {
|
|
709
676
|
this.#clearScreen();
|
|
710
|
-
this
|
|
677
|
+
this.ui.logger.log(`${this.ui.colors.green(action)} ${filePath}`);
|
|
678
|
+
this.#restartHTTPServer();
|
|
679
|
+
return;
|
|
711
680
|
}
|
|
681
|
+
this.ui.logger.log(`${this.ui.colors.green(action)} ${filePath}`);
|
|
712
682
|
}
|
|
713
683
|
/**
|
|
714
|
-
*
|
|
684
|
+
* Registers inline hooks for the file changes and restarts the
|
|
685
|
+
* HTTP server when a file gets changed.
|
|
715
686
|
*/
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
this.#
|
|
719
|
-
|
|
720
|
-
|
|
687
|
+
#registerServerRestartHooks() {
|
|
688
|
+
this.#hooks.add("fileAdded", (filePath) => this.#handleFileChange(filePath, "add"));
|
|
689
|
+
this.#hooks.add(
|
|
690
|
+
"fileChanged",
|
|
691
|
+
(filePath, hotReplaced) => this.#handleFileChange(filePath, "update", hotReplaced)
|
|
692
|
+
);
|
|
693
|
+
this.#hooks.add("fileRemoved", (filePath) => this.#handleFileChange(filePath, "delete"));
|
|
721
694
|
}
|
|
722
695
|
/**
|
|
723
|
-
*
|
|
696
|
+
* Starts the HTTP server
|
|
724
697
|
*/
|
|
725
|
-
|
|
726
|
-
this.#
|
|
727
|
-
this
|
|
728
|
-
return
|
|
698
|
+
async #startHTTPServer(port) {
|
|
699
|
+
await this.#hooks.runner("devServerStarting").run(this);
|
|
700
|
+
debug_default('starting http server using "%s" file, options %O', this.scriptFile, this.options);
|
|
701
|
+
return new Promise(async (resolve) => {
|
|
702
|
+
this.#httpServer = runNode(this.cwd, {
|
|
703
|
+
script: this.scriptFile,
|
|
704
|
+
env: { PORT: port, ...this.options.env },
|
|
705
|
+
nodeArgs: this.options.nodeArgs,
|
|
706
|
+
reject: true,
|
|
707
|
+
scriptArgs: this.options.scriptArgs
|
|
708
|
+
});
|
|
709
|
+
this.#httpServer.on("message", async (message) => {
|
|
710
|
+
if (this.#isAdonisJSReadyMessage(message)) {
|
|
711
|
+
await this.#postServerReady(message);
|
|
712
|
+
resolve();
|
|
713
|
+
} else if (this.#mode === "hmr" && this.#isHotHookMessage(message)) {
|
|
714
|
+
if (message.type === "hot-hook:file-changed") {
|
|
715
|
+
switch (message.action) {
|
|
716
|
+
case "add":
|
|
717
|
+
this.#hooks.runner("fileAdded").run(string3.toUnixSlash(message.path), this);
|
|
718
|
+
break;
|
|
719
|
+
case "change":
|
|
720
|
+
this.#hooks.runner("fileChanged").run(string3.toUnixSlash(message.path), false, this);
|
|
721
|
+
break;
|
|
722
|
+
case "unlink":
|
|
723
|
+
this.#hooks.runner("fileRemoved").run(string3.toUnixSlash(message.path), this);
|
|
724
|
+
}
|
|
725
|
+
} else if (message.type === "hot-hook:full-reload") {
|
|
726
|
+
this.#hooks.runner("fileChanged").run(string3.toUnixSlash(message.path), false, this);
|
|
727
|
+
} else if (message.type === "hot-hook:invalidated") {
|
|
728
|
+
this.#hooks.runner("fileChanged").run(string3.toUnixSlash(message.path), true, this);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
this.#httpServer.then((result) => {
|
|
733
|
+
if (!this.#watcher) {
|
|
734
|
+
this.#onClose?.(result.exitCode);
|
|
735
|
+
} else {
|
|
736
|
+
this.ui.logger.info("Underlying HTTP server closed. Still watching for changes");
|
|
737
|
+
}
|
|
738
|
+
}).catch((error) => {
|
|
739
|
+
if (!this.#watcher) {
|
|
740
|
+
this.#onError?.(error);
|
|
741
|
+
} else {
|
|
742
|
+
this.ui.logger.info("Underlying HTTP server died. Still watching for changes");
|
|
743
|
+
}
|
|
744
|
+
}).finally(() => {
|
|
745
|
+
console.log("ere>>");
|
|
746
|
+
resolve();
|
|
747
|
+
});
|
|
748
|
+
});
|
|
729
749
|
}
|
|
730
750
|
/**
|
|
731
751
|
* Add listener to get notified when dev server is
|
|
@@ -744,11 +764,10 @@ var DevServer = class {
|
|
|
744
764
|
return this;
|
|
745
765
|
}
|
|
746
766
|
/**
|
|
747
|
-
* Close watchers and running child
|
|
767
|
+
* Close watchers and the running child process
|
|
748
768
|
*/
|
|
749
769
|
async close() {
|
|
750
770
|
await this.#watcher?.close();
|
|
751
|
-
this.#assetsServer?.stop();
|
|
752
771
|
if (this.#httpServer) {
|
|
753
772
|
this.#httpServer.removeAllListeners();
|
|
754
773
|
this.#httpServer.kill("SIGKILL");
|
|
@@ -757,173 +776,175 @@ var DevServer = class {
|
|
|
757
776
|
/**
|
|
758
777
|
* Start the development server
|
|
759
778
|
*/
|
|
760
|
-
async start() {
|
|
761
|
-
|
|
779
|
+
async start(ts) {
|
|
780
|
+
const tsConfig = parseConfig(this.cwd, ts);
|
|
781
|
+
if (!tsConfig) {
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
this.#stickyPort = String(await getPort(this.cwd));
|
|
785
|
+
this.#fileSystem = new FileSystem(this.cwd, tsConfig, this.options);
|
|
786
|
+
this.#hooks = await loadHooks(this.options.hooks, [
|
|
787
|
+
"devServerStarting",
|
|
788
|
+
"devServerStarted",
|
|
789
|
+
"fileAdded",
|
|
790
|
+
"fileChanged",
|
|
791
|
+
"fileRemoved"
|
|
792
|
+
]);
|
|
793
|
+
this.#registerServerRestartHooks();
|
|
794
|
+
if (this.options.hmr) {
|
|
795
|
+
this.#mode = "hmr";
|
|
796
|
+
this.options.nodeArgs = this.options.nodeArgs.concat("--import=hot-hook/register");
|
|
797
|
+
this.options.env = {
|
|
798
|
+
...this.options.env,
|
|
799
|
+
HOT_HOOK_INCLUDE: this.#fileSystem.includes.join(","),
|
|
800
|
+
HOT_HOOK_IGNORE: this.#fileSystem.excludes.join(","),
|
|
801
|
+
HOT_HOOK_RESTART: (this.options.metaFiles ?? []).map(({ pattern }) => pattern).join(",")
|
|
802
|
+
};
|
|
803
|
+
}
|
|
762
804
|
this.#clearScreen();
|
|
763
|
-
this
|
|
764
|
-
this.#startHTTPServer(
|
|
765
|
-
this.#startAssetsServer();
|
|
805
|
+
this.ui.logger.info("starting HTTP server...");
|
|
806
|
+
await this.#startHTTPServer(this.#stickyPort);
|
|
766
807
|
}
|
|
767
808
|
/**
|
|
768
809
|
* Start the development server in watch mode
|
|
769
810
|
*/
|
|
770
811
|
async startAndWatch(ts, options) {
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
this.#isWatching = true;
|
|
774
|
-
this.#clearScreen();
|
|
775
|
-
this.#logger.info("starting HTTP server...");
|
|
776
|
-
this.#startHTTPServer(port, "blocking");
|
|
777
|
-
this.#startAssetsServer();
|
|
778
|
-
const output = watch(this.#cwd, ts, options || {});
|
|
779
|
-
if (!output) {
|
|
780
|
-
this.#onClose?.(1);
|
|
812
|
+
const tsConfig = parseConfig(this.cwd, ts);
|
|
813
|
+
if (!tsConfig) {
|
|
781
814
|
return;
|
|
782
815
|
}
|
|
783
|
-
this.#
|
|
784
|
-
|
|
785
|
-
|
|
816
|
+
this.#mode = "watch";
|
|
817
|
+
this.#stickyPort = String(await getPort(this.cwd));
|
|
818
|
+
this.#fileSystem = new FileSystem(this.cwd, tsConfig, this.options);
|
|
819
|
+
this.#hooks = await loadHooks(this.options.hooks, [
|
|
820
|
+
"devServerStarting",
|
|
821
|
+
"devServerStarted",
|
|
822
|
+
"fileAdded",
|
|
823
|
+
"fileChanged",
|
|
824
|
+
"fileRemoved"
|
|
825
|
+
]);
|
|
826
|
+
this.#registerServerRestartHooks();
|
|
827
|
+
this.#clearScreen();
|
|
828
|
+
this.ui.logger.info("starting HTTP server...");
|
|
829
|
+
await this.#startHTTPServer(this.#stickyPort);
|
|
830
|
+
this.#watcher = watch({
|
|
831
|
+
usePolling: options?.poll ?? false,
|
|
832
|
+
cwd: fileURLToPath4(this.cwd),
|
|
833
|
+
ignoreInitial: true,
|
|
834
|
+
ignored: (file, stats) => {
|
|
835
|
+
if (!stats) {
|
|
836
|
+
return false;
|
|
837
|
+
}
|
|
838
|
+
if (stats.isFile()) {
|
|
839
|
+
return !this.#fileSystem.shouldWatchFile(file);
|
|
840
|
+
}
|
|
841
|
+
return !this.#fileSystem.shouldWatchDirectory(file);
|
|
842
|
+
}
|
|
843
|
+
});
|
|
844
|
+
this.#watcher.on("ready", () => {
|
|
845
|
+
this.ui.logger.info("watching file system for changes...");
|
|
786
846
|
});
|
|
787
|
-
|
|
788
|
-
this
|
|
789
|
-
this
|
|
847
|
+
this.#watcher.on("error", (error) => {
|
|
848
|
+
this.ui.logger.warning("file system watcher failure");
|
|
849
|
+
this.ui.logger.fatal(error);
|
|
790
850
|
this.#onError?.(error);
|
|
791
|
-
|
|
851
|
+
this.#watcher?.close();
|
|
792
852
|
});
|
|
793
|
-
|
|
794
|
-
"source:add",
|
|
795
|
-
({ relativePath }) => this.#handleSourceFileChange("add", port, relativePath)
|
|
796
|
-
);
|
|
797
|
-
output.watcher.on(
|
|
798
|
-
"source:change",
|
|
799
|
-
({ relativePath }) => this.#handleSourceFileChange("update", port, relativePath)
|
|
800
|
-
);
|
|
801
|
-
output.watcher.on(
|
|
802
|
-
"source:unlink",
|
|
803
|
-
({ relativePath }) => this.#handleSourceFileChange("delete", port, relativePath)
|
|
804
|
-
);
|
|
805
|
-
output.watcher.on(
|
|
853
|
+
this.#watcher.on(
|
|
806
854
|
"add",
|
|
807
|
-
(
|
|
855
|
+
(filePath) => this.#hooks.runner("fileAdded").run(string3.toUnixSlash(filePath), this)
|
|
808
856
|
);
|
|
809
|
-
|
|
857
|
+
this.#watcher.on(
|
|
810
858
|
"change",
|
|
811
|
-
(
|
|
859
|
+
(filePath) => this.#hooks.runner("fileChanged").run(string3.toUnixSlash(filePath), false, this)
|
|
812
860
|
);
|
|
813
|
-
|
|
861
|
+
this.#watcher.on(
|
|
814
862
|
"unlink",
|
|
815
|
-
(
|
|
863
|
+
(filePath) => this.#hooks.runner("fileRemoved").run(string3.toUnixSlash(filePath), this)
|
|
816
864
|
);
|
|
817
865
|
}
|
|
818
866
|
};
|
|
819
867
|
|
|
820
868
|
// src/test_runner.ts
|
|
821
|
-
import
|
|
822
|
-
import {
|
|
823
|
-
|
|
869
|
+
import { cliui as cliui3 } from "@poppinss/cliui";
|
|
870
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
871
|
+
import string4 from "@poppinss/utils/string";
|
|
824
872
|
var TestRunner = class {
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
* The script file to run as a child process
|
|
830
|
-
*/
|
|
831
|
-
#scriptFile = "bin/test.js";
|
|
832
|
-
/**
|
|
833
|
-
* Pico matcher function to check if the filepath is
|
|
834
|
-
* part of the `metaFiles` glob patterns
|
|
835
|
-
*/
|
|
836
|
-
#isMetaFile;
|
|
873
|
+
constructor(cwd, options) {
|
|
874
|
+
this.cwd = cwd;
|
|
875
|
+
this.options = options;
|
|
876
|
+
}
|
|
837
877
|
/**
|
|
838
|
-
*
|
|
839
|
-
*
|
|
878
|
+
* External listeners that are invoked when child process
|
|
879
|
+
* gets an error or closes
|
|
840
880
|
*/
|
|
841
|
-
#
|
|
881
|
+
#onError;
|
|
882
|
+
#onClose;
|
|
842
883
|
/**
|
|
843
|
-
*
|
|
884
|
+
* The stickyPort is set by the startAndWatch method and we will
|
|
885
|
+
* continue to use this port during re-runs
|
|
844
886
|
*/
|
|
845
|
-
#
|
|
887
|
+
#stickyPort;
|
|
846
888
|
/**
|
|
847
|
-
*
|
|
848
|
-
* command. In watch mode, we will append an additional
|
|
849
|
-
* filter to run tests only for the file that has been
|
|
850
|
-
* changed.
|
|
889
|
+
* Reference to chokidar watcher
|
|
851
890
|
*/
|
|
852
|
-
#
|
|
891
|
+
#watcher;
|
|
853
892
|
/**
|
|
854
|
-
*
|
|
855
|
-
* set of tests to finish before triggering a re-run. Therefore,
|
|
856
|
-
* we use this flag to know if we are already busy in running
|
|
857
|
-
* tests and ignore file-changes.
|
|
893
|
+
* Reference to the test script child process
|
|
858
894
|
*/
|
|
859
|
-
#
|
|
895
|
+
#testsProcess;
|
|
860
896
|
/**
|
|
861
|
-
*
|
|
862
|
-
*
|
|
897
|
+
* Filesystem is used to decide which files to watch or entertain in watch
|
|
898
|
+
* mode
|
|
863
899
|
*/
|
|
864
|
-
#
|
|
865
|
-
#onClose;
|
|
900
|
+
#fileSystem;
|
|
866
901
|
/**
|
|
867
|
-
*
|
|
902
|
+
* Hooks to execute custom actions during the tests runner lifecycle
|
|
868
903
|
*/
|
|
869
|
-
#
|
|
904
|
+
#hooks;
|
|
870
905
|
/**
|
|
871
|
-
*
|
|
906
|
+
* Re-runs the test child process and throttle concurrent calls to
|
|
907
|
+
* ensure we do not end up with a long loop of restarts
|
|
872
908
|
*/
|
|
873
|
-
#
|
|
909
|
+
#reRunTests = throttle(async (filters) => {
|
|
910
|
+
if (this.#testsProcess) {
|
|
911
|
+
this.#testsProcess.removeAllListeners();
|
|
912
|
+
this.#testsProcess.kill("SIGKILL");
|
|
913
|
+
}
|
|
914
|
+
await this.#runTests(this.#stickyPort, filters);
|
|
915
|
+
}, "reRunTests");
|
|
874
916
|
/**
|
|
875
|
-
*
|
|
917
|
+
* CLI UI to log colorful messages
|
|
876
918
|
*/
|
|
877
|
-
|
|
919
|
+
ui = cliui3();
|
|
878
920
|
/**
|
|
879
|
-
*
|
|
921
|
+
* The script file to run as a child process
|
|
880
922
|
*/
|
|
881
|
-
|
|
882
|
-
return this.#logger.getColors();
|
|
883
|
-
}
|
|
884
|
-
constructor(cwd, options) {
|
|
885
|
-
this.#cwd = cwd;
|
|
886
|
-
this.#options = options;
|
|
887
|
-
this.#isMetaFile = picomatch2((this.#options.metaFiles || []).map(({ pattern }) => pattern));
|
|
888
|
-
this.#isTestFile = picomatch2(
|
|
889
|
-
this.#options.suites.filter((suite) => {
|
|
890
|
-
if (this.#options.filters.suites) {
|
|
891
|
-
return this.#options.filters.suites.includes(suite.name);
|
|
892
|
-
}
|
|
893
|
-
return true;
|
|
894
|
-
}).map((suite) => suite.files).flat(1)
|
|
895
|
-
);
|
|
896
|
-
this.#scriptArgs = this.#convertOptionsToArgs().concat(this.#options.scriptArgs);
|
|
897
|
-
this.#initialFiltersArgs = this.#convertFiltersToArgs(this.#options.filters);
|
|
898
|
-
}
|
|
923
|
+
scriptFile = "bin/test.ts";
|
|
899
924
|
/**
|
|
900
925
|
* Convert test runner options to the CLI args
|
|
901
926
|
*/
|
|
902
927
|
#convertOptionsToArgs() {
|
|
903
928
|
const args = [];
|
|
904
|
-
if (this
|
|
929
|
+
if (this.options.reporters) {
|
|
905
930
|
args.push("--reporters");
|
|
906
|
-
args.push(this
|
|
931
|
+
args.push(this.options.reporters.join(","));
|
|
907
932
|
}
|
|
908
|
-
if (this
|
|
933
|
+
if (this.options.timeout !== void 0) {
|
|
909
934
|
args.push("--timeout");
|
|
910
|
-
args.push(String(this
|
|
935
|
+
args.push(String(this.options.timeout));
|
|
911
936
|
}
|
|
912
|
-
if (this
|
|
937
|
+
if (this.options.failed) {
|
|
913
938
|
args.push("--failed");
|
|
914
939
|
}
|
|
915
|
-
if (this
|
|
940
|
+
if (this.options.retries !== void 0) {
|
|
916
941
|
args.push("--retries");
|
|
917
|
-
args.push(String(this
|
|
942
|
+
args.push(String(this.options.retries));
|
|
918
943
|
}
|
|
919
944
|
return args;
|
|
920
945
|
}
|
|
921
946
|
/**
|
|
922
947
|
* Converts all known filters to CLI args.
|
|
923
|
-
*
|
|
924
|
-
* The following code snippet may seem like repetitive code. But, it
|
|
925
|
-
* is done intentionally to have visibility around how each filter
|
|
926
|
-
* is converted to an arg.
|
|
927
948
|
*/
|
|
928
949
|
#convertFiltersToArgs(filters) {
|
|
929
950
|
const args = [];
|
|
@@ -952,93 +973,74 @@ var TestRunner = class {
|
|
|
952
973
|
* Conditionally clear the terminal screen
|
|
953
974
|
*/
|
|
954
975
|
#clearScreen() {
|
|
955
|
-
if (this
|
|
976
|
+
if (this.options.clearScreen) {
|
|
956
977
|
process.stdout.write("\x1Bc");
|
|
957
978
|
}
|
|
958
979
|
}
|
|
959
980
|
/**
|
|
960
981
|
* Runs tests
|
|
961
982
|
*/
|
|
962
|
-
#runTests(port,
|
|
963
|
-
this.#
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
this
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
+
async #runTests(port, filters) {
|
|
984
|
+
await this.#hooks.runner("testsStarting").run(this);
|
|
985
|
+
debug_default('running tests using "%s" file, options %O', this.scriptFile, this.options);
|
|
986
|
+
return new Promise(async (resolve) => {
|
|
987
|
+
const scriptArgs = this.#convertOptionsToArgs().concat(this.options.scriptArgs).concat(
|
|
988
|
+
this.#convertFiltersToArgs({
|
|
989
|
+
...this.options.filters,
|
|
990
|
+
...filters
|
|
991
|
+
})
|
|
992
|
+
);
|
|
993
|
+
this.#testsProcess = runNode(this.cwd, {
|
|
994
|
+
script: this.scriptFile,
|
|
995
|
+
reject: true,
|
|
996
|
+
env: { PORT: port, ...this.options.env },
|
|
997
|
+
nodeArgs: this.options.nodeArgs,
|
|
998
|
+
scriptArgs
|
|
999
|
+
});
|
|
1000
|
+
this.#testsProcess.then((result) => {
|
|
1001
|
+
this.#hooks.runner("testsFinished").run(this).catch((error) => {
|
|
1002
|
+
this.ui.logger.error('One of the "testsFinished" hooks failed');
|
|
1003
|
+
this.ui.logger.fatal(error);
|
|
1004
|
+
}).finally(() => {
|
|
1005
|
+
if (!this.#watcher) {
|
|
1006
|
+
this.#onClose?.(result.exitCode);
|
|
1007
|
+
this.close();
|
|
1008
|
+
}
|
|
1009
|
+
});
|
|
1010
|
+
}).catch((error) => {
|
|
1011
|
+
if (!this.#watcher) {
|
|
1012
|
+
this.#onError?.(error);
|
|
1013
|
+
this.close();
|
|
1014
|
+
} else {
|
|
1015
|
+
this.ui.logger.info("Underlying HTTP server died. Still watching for changes");
|
|
1016
|
+
}
|
|
1017
|
+
}).finally(() => resolve());
|
|
983
1018
|
});
|
|
984
1019
|
}
|
|
985
1020
|
/**
|
|
986
|
-
*
|
|
987
|
-
* executed in watch mode only.
|
|
988
|
-
*/
|
|
989
|
-
#rerunTests(port, filters) {
|
|
990
|
-
if (this.#testScript) {
|
|
991
|
-
this.#testScript.removeAllListeners();
|
|
992
|
-
this.#testScript.kill("SIGKILL");
|
|
993
|
-
}
|
|
994
|
-
this.#runTests(port, "blocking", filters);
|
|
995
|
-
}
|
|
996
|
-
/**
|
|
997
|
-
* Starts the assets server
|
|
998
|
-
*/
|
|
999
|
-
#startAssetsServer() {
|
|
1000
|
-
this.#assetsServer = new AssetsDevServer(this.#cwd, this.#options.assets);
|
|
1001
|
-
this.#assetsServer.setLogger(this.#logger);
|
|
1002
|
-
this.#assetsServer.start();
|
|
1003
|
-
}
|
|
1004
|
-
/**
|
|
1005
|
-
* Handles a non TypeScript file change
|
|
1006
|
-
*/
|
|
1007
|
-
#handleFileChange(action, port, relativePath) {
|
|
1008
|
-
if (this.#isBusy) {
|
|
1009
|
-
return;
|
|
1010
|
-
}
|
|
1011
|
-
if (isDotEnvFile(relativePath) || this.#isMetaFile(relativePath)) {
|
|
1012
|
-
this.#clearScreen();
|
|
1013
|
-
this.#logger.log(`${this.#colors.green(action)} ${relativePath}`);
|
|
1014
|
-
this.#rerunTests(port);
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
/**
|
|
1018
|
-
* Handles TypeScript source file change
|
|
1021
|
+
* Handles file change event
|
|
1019
1022
|
*/
|
|
1020
|
-
#
|
|
1021
|
-
|
|
1023
|
+
#handleFileChange(filePath, action) {
|
|
1024
|
+
const file = this.#fileSystem.inspect(filePath);
|
|
1025
|
+
if (!file) {
|
|
1022
1026
|
return;
|
|
1023
1027
|
}
|
|
1024
1028
|
this.#clearScreen();
|
|
1025
|
-
this
|
|
1026
|
-
if (
|
|
1027
|
-
this.#
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
});
|
|
1031
|
-
return;
|
|
1029
|
+
this.ui.logger.log(`${this.ui.colors.green(action)} ${filePath}`);
|
|
1030
|
+
if (file.fileType === "test") {
|
|
1031
|
+
this.#reRunTests({ files: [filePath] });
|
|
1032
|
+
} else {
|
|
1033
|
+
this.#reRunTests();
|
|
1032
1034
|
}
|
|
1033
|
-
this.#rerunTests(port);
|
|
1034
1035
|
}
|
|
1035
1036
|
/**
|
|
1036
|
-
*
|
|
1037
|
+
* Registers inline hooks for the file changes and restarts the
|
|
1038
|
+
* HTTP server when a file gets changed.
|
|
1037
1039
|
*/
|
|
1038
|
-
|
|
1039
|
-
this.#
|
|
1040
|
-
this.#
|
|
1041
|
-
|
|
1040
|
+
#registerServerRestartHooks() {
|
|
1041
|
+
this.#hooks.add("fileAdded", (filePath) => this.#handleFileChange(filePath, "add"));
|
|
1042
|
+
this.#hooks.add("fileChanged", (filePath) => this.#handleFileChange(filePath, "update"));
|
|
1043
|
+
this.#hooks.add("fileRemoved", (filePath) => this.#handleFileChange(filePath, "delete"));
|
|
1042
1044
|
}
|
|
1043
1045
|
/**
|
|
1044
1046
|
* Add listener to get notified when dev server is
|
|
@@ -1061,69 +1063,90 @@ var TestRunner = class {
|
|
|
1061
1063
|
*/
|
|
1062
1064
|
async close() {
|
|
1063
1065
|
await this.#watcher?.close();
|
|
1064
|
-
this.#
|
|
1065
|
-
|
|
1066
|
-
this.#
|
|
1067
|
-
this.#testScript.kill("SIGKILL");
|
|
1066
|
+
if (this.#testsProcess) {
|
|
1067
|
+
this.#testsProcess.removeAllListeners();
|
|
1068
|
+
this.#testsProcess.kill("SIGKILL");
|
|
1068
1069
|
}
|
|
1069
1070
|
}
|
|
1070
1071
|
/**
|
|
1071
1072
|
* Runs tests
|
|
1072
1073
|
*/
|
|
1073
1074
|
async run() {
|
|
1074
|
-
|
|
1075
|
+
this.#stickyPort = String(await getPort(this.cwd));
|
|
1076
|
+
this.#hooks = await loadHooks(this.options.hooks, [
|
|
1077
|
+
"testsStarting",
|
|
1078
|
+
"testsFinished",
|
|
1079
|
+
"fileAdded",
|
|
1080
|
+
"fileChanged",
|
|
1081
|
+
"fileRemoved"
|
|
1082
|
+
]);
|
|
1075
1083
|
this.#clearScreen();
|
|
1076
|
-
this
|
|
1077
|
-
this.#
|
|
1078
|
-
this.#runTests(port, "nonblocking");
|
|
1084
|
+
this.ui.logger.info("booting application to run tests...");
|
|
1085
|
+
await this.#runTests(this.#stickyPort);
|
|
1079
1086
|
}
|
|
1080
1087
|
/**
|
|
1081
1088
|
* Run tests in watch mode
|
|
1082
1089
|
*/
|
|
1083
1090
|
async runAndWatch(ts, options) {
|
|
1084
|
-
const
|
|
1085
|
-
|
|
1086
|
-
this.#startAssetsServer();
|
|
1087
|
-
this.#logger.info("booting application to run tests...");
|
|
1088
|
-
this.#runTests(port, "blocking");
|
|
1089
|
-
const output = watch(this.#cwd, ts, options || {});
|
|
1090
|
-
if (!output) {
|
|
1091
|
-
this.#onClose?.(1);
|
|
1091
|
+
const tsConfig = parseConfig(this.cwd, ts);
|
|
1092
|
+
if (!tsConfig) {
|
|
1092
1093
|
return;
|
|
1093
1094
|
}
|
|
1094
|
-
this.#
|
|
1095
|
-
|
|
1096
|
-
this
|
|
1095
|
+
this.#stickyPort = String(await getPort(this.cwd));
|
|
1096
|
+
this.#fileSystem = new FileSystem(this.cwd, tsConfig, {
|
|
1097
|
+
...this.options,
|
|
1098
|
+
suites: this.options.suites?.filter((suite) => {
|
|
1099
|
+
if (this.options.filters.suites) {
|
|
1100
|
+
return this.options.filters.suites.includes(suite.name);
|
|
1101
|
+
}
|
|
1102
|
+
return true;
|
|
1103
|
+
})
|
|
1097
1104
|
});
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1105
|
+
this.#hooks = await loadHooks(this.options.hooks, [
|
|
1106
|
+
"testsStarting",
|
|
1107
|
+
"testsFinished",
|
|
1108
|
+
"fileAdded",
|
|
1109
|
+
"fileChanged",
|
|
1110
|
+
"fileRemoved"
|
|
1111
|
+
]);
|
|
1112
|
+
this.#registerServerRestartHooks();
|
|
1113
|
+
this.#clearScreen();
|
|
1114
|
+
this.ui.logger.info("booting application to run tests...");
|
|
1115
|
+
await this.#runTests(this.#stickyPort);
|
|
1116
|
+
this.#watcher = watch({
|
|
1117
|
+
usePolling: options?.poll ?? false,
|
|
1118
|
+
cwd: fileURLToPath5(this.cwd),
|
|
1119
|
+
ignoreInitial: true,
|
|
1120
|
+
ignored: (file, stats) => {
|
|
1121
|
+
if (!stats) {
|
|
1122
|
+
return false;
|
|
1123
|
+
}
|
|
1124
|
+
if (stats.isFile()) {
|
|
1125
|
+
return !this.#fileSystem.shouldWatchFile(file);
|
|
1126
|
+
}
|
|
1127
|
+
return !this.#fileSystem.shouldWatchDirectory(file);
|
|
1128
|
+
}
|
|
1129
|
+
});
|
|
1130
|
+
this.#watcher.on("ready", () => {
|
|
1131
|
+
this.ui.logger.info("watching file system for changes...");
|
|
1132
|
+
});
|
|
1133
|
+
this.#watcher.on("error", (error) => {
|
|
1134
|
+
this.ui.logger.warning("file system watcher failure");
|
|
1135
|
+
this.ui.logger.fatal(error);
|
|
1101
1136
|
this.#onError?.(error);
|
|
1102
|
-
|
|
1137
|
+
this.#watcher?.close();
|
|
1103
1138
|
});
|
|
1104
|
-
|
|
1105
|
-
"source:add",
|
|
1106
|
-
({ relativePath }) => this.#handleSourceFileChange("add", port, relativePath)
|
|
1107
|
-
);
|
|
1108
|
-
output.watcher.on(
|
|
1109
|
-
"source:change",
|
|
1110
|
-
({ relativePath }) => this.#handleSourceFileChange("update", port, relativePath)
|
|
1111
|
-
);
|
|
1112
|
-
output.watcher.on(
|
|
1113
|
-
"source:unlink",
|
|
1114
|
-
({ relativePath }) => this.#handleSourceFileChange("delete", port, relativePath)
|
|
1115
|
-
);
|
|
1116
|
-
output.watcher.on(
|
|
1139
|
+
this.#watcher.on(
|
|
1117
1140
|
"add",
|
|
1118
|
-
(
|
|
1141
|
+
(filePath) => this.#hooks.runner("fileAdded").run(string4.toUnixSlash(filePath), this)
|
|
1119
1142
|
);
|
|
1120
|
-
|
|
1143
|
+
this.#watcher.on(
|
|
1121
1144
|
"change",
|
|
1122
|
-
(
|
|
1145
|
+
(filePath) => this.#hooks.runner("fileChanged").run(string4.toUnixSlash(filePath), false, this)
|
|
1123
1146
|
);
|
|
1124
|
-
|
|
1147
|
+
this.#watcher.on(
|
|
1125
1148
|
"unlink",
|
|
1126
|
-
(
|
|
1149
|
+
(filePath) => this.#hooks.runner("fileRemoved").run(string4.toUnixSlash(filePath), this)
|
|
1127
1150
|
);
|
|
1128
1151
|
}
|
|
1129
1152
|
};
|
|
@@ -1132,4 +1155,3 @@ export {
|
|
|
1132
1155
|
DevServer,
|
|
1133
1156
|
TestRunner
|
|
1134
1157
|
};
|
|
1135
|
-
//# sourceMappingURL=index.js.map
|