@adonisjs/assembler 7.8.2 → 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 -670
- 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,33 +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
|
-
reject: true,
|
|
281
|
-
script: assetsBundler.cmd,
|
|
282
|
-
scriptArgs: assetsBundler.args
|
|
283
|
-
});
|
|
284
|
-
return true;
|
|
285
|
-
} catch {
|
|
286
|
-
return false;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
230
|
/**
|
|
290
231
|
* Runs tsc command to build the source.
|
|
291
232
|
*/
|
|
292
233
|
async #runTsc(outDir) {
|
|
293
234
|
try {
|
|
294
|
-
await run(this
|
|
235
|
+
await run(this.cwd, {
|
|
295
236
|
stdio: "inherit",
|
|
296
237
|
script: "tsc",
|
|
297
238
|
scriptArgs: ["--outDir", outDir]
|
|
@@ -305,26 +246,21 @@ var Bundler = class {
|
|
|
305
246
|
* Copy meta files to the output directory
|
|
306
247
|
*/
|
|
307
248
|
async #copyMetaFiles(outDir, additionalFilesToCopy) {
|
|
308
|
-
const metaFiles = (this
|
|
249
|
+
const metaFiles = (this.options.metaFiles || []).map((file) => file.pattern).concat(additionalFilesToCopy);
|
|
309
250
|
await copyFiles(metaFiles, this.#cwdPath, outDir);
|
|
310
251
|
}
|
|
311
252
|
/**
|
|
312
253
|
* Detect the package manager used by the project
|
|
313
|
-
* and return the lockfile name and install command
|
|
314
|
-
* related to it.
|
|
315
254
|
*/
|
|
316
|
-
async #
|
|
317
|
-
|
|
318
|
-
if (
|
|
319
|
-
|
|
255
|
+
async #detectPackageManager() {
|
|
256
|
+
const pkgManager = await detectPackageManager(this.#cwdPath);
|
|
257
|
+
if (pkgManager === "deno") {
|
|
258
|
+
return "npm";
|
|
320
259
|
}
|
|
321
|
-
if (
|
|
322
|
-
|
|
260
|
+
if (pkgManager === "pnpm@6") {
|
|
261
|
+
return "pnpm";
|
|
323
262
|
}
|
|
324
|
-
|
|
325
|
-
return null;
|
|
326
|
-
}
|
|
327
|
-
return SUPPORT_PACKAGE_MANAGERS[pkgManager];
|
|
263
|
+
return pkgManager;
|
|
328
264
|
}
|
|
329
265
|
/**
|
|
330
266
|
* Rewrite the ace file since the original one
|
|
@@ -346,258 +282,346 @@ var Bundler = class {
|
|
|
346
282
|
`
|
|
347
283
|
);
|
|
348
284
|
await fs.writeFile(aceFileLocation, aceFileContent);
|
|
349
|
-
this
|
|
350
|
-
}
|
|
351
|
-
/**
|
|
352
|
-
* Set a custom CLI UI logger
|
|
353
|
-
*/
|
|
354
|
-
setLogger(logger) {
|
|
355
|
-
this.#logger = logger;
|
|
356
|
-
return this;
|
|
285
|
+
this.ui.logger.info("created ace file", { suffix: this.#getRelativeName(aceFileLocation) });
|
|
357
286
|
}
|
|
358
287
|
/**
|
|
359
288
|
* Bundles the application to be run in production
|
|
360
289
|
*/
|
|
361
290
|
async bundle(stopOnError = true, client) {
|
|
362
|
-
await this
|
|
363
|
-
|
|
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);
|
|
364
294
|
if (!config) {
|
|
365
295
|
return false;
|
|
366
296
|
}
|
|
367
|
-
const outDir = config.options.outDir || fileURLToPath2(new URL("build/", this
|
|
368
|
-
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) });
|
|
369
299
|
await this.#cleanupBuildDirectory(outDir);
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
}
|
|
373
|
-
await this.#hooks.onBuildStarting({ colors: ui.colors, logger: this.#logger });
|
|
374
|
-
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" });
|
|
375
302
|
const buildCompleted = await this.#runTsc(outDir);
|
|
376
303
|
await this.#createAceFile(outDir);
|
|
377
304
|
if (!buildCompleted && stopOnError) {
|
|
378
305
|
await this.#cleanupBuildDirectory(outDir);
|
|
379
|
-
const instructions = ui.sticker().fullScreen().drawBorder((borderChar, colors) => colors.red(borderChar));
|
|
306
|
+
const instructions = this.ui.sticker().fullScreen().drawBorder((borderChar, colors) => colors.red(borderChar));
|
|
380
307
|
instructions.add(
|
|
381
|
-
this
|
|
308
|
+
this.ui.colors.red("Cannot complete the build process as there are TypeScript errors.")
|
|
382
309
|
);
|
|
383
310
|
instructions.add(
|
|
384
|
-
this
|
|
311
|
+
this.ui.colors.red(
|
|
385
312
|
'Use "--ignore-ts-errors" flag to ignore TypeScript errors and continue the build.'
|
|
386
313
|
)
|
|
387
314
|
);
|
|
388
|
-
this
|
|
315
|
+
this.ui.logger.logError(instructions.prepare());
|
|
389
316
|
return false;
|
|
390
317
|
}
|
|
391
|
-
const
|
|
392
|
-
|
|
393
|
-
|
|
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");
|
|
394
323
|
await this.#copyMetaFiles(outDir, pkgFiles);
|
|
395
|
-
this
|
|
396
|
-
this
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
pkgManager ? pkgManager.installCommand : "Install production dependencies"
|
|
401
|
-
)
|
|
402
|
-
).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();
|
|
403
329
|
return true;
|
|
404
330
|
}
|
|
405
331
|
};
|
|
406
332
|
|
|
407
333
|
// src/dev_server.ts
|
|
408
|
-
import
|
|
409
|
-
import { relative as relative3 } from "node:path";
|
|
334
|
+
import { cliui as cliui2 } from "@poppinss/cliui";
|
|
410
335
|
import prettyHrtime from "pretty-hrtime";
|
|
411
|
-
import { fileURLToPath as
|
|
412
|
-
import
|
|
336
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
337
|
+
import string3 from "@poppinss/utils/string";
|
|
413
338
|
|
|
414
|
-
// src/
|
|
415
|
-
import
|
|
416
|
-
|
|
417
|
-
|
|
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
|
+
*/
|
|
418
351
|
#cwd;
|
|
419
|
-
#logger = ui2.logger;
|
|
420
|
-
#options;
|
|
421
|
-
#devServer;
|
|
422
352
|
/**
|
|
423
|
-
*
|
|
353
|
+
* Referenced to the parsed ts config file. We use it to read the includes,
|
|
354
|
+
* excludes and pre-scanned files.
|
|
424
355
|
*/
|
|
425
|
-
|
|
426
|
-
return this.#logger.getColors();
|
|
427
|
-
}
|
|
428
|
-
constructor(cwd, options) {
|
|
429
|
-
this.#cwd = cwd;
|
|
430
|
-
this.#options = options;
|
|
431
|
-
}
|
|
356
|
+
#tsConfig;
|
|
432
357
|
/**
|
|
433
|
-
*
|
|
358
|
+
* Set of pre-scanned typeScript files provided by tsconfig
|
|
434
359
|
*/
|
|
435
|
-
#
|
|
436
|
-
const dataString = data.toString();
|
|
437
|
-
const lines = dataString.split("\n");
|
|
438
|
-
if (dataString.includes("Local") && dataString.includes("Network")) {
|
|
439
|
-
const sticker = ui2.sticker().useColors(this.#colors).useRenderer(this.#logger.getRenderer());
|
|
440
|
-
lines.forEach((line) => {
|
|
441
|
-
if (line.trim()) {
|
|
442
|
-
sticker.add(line);
|
|
443
|
-
}
|
|
444
|
-
});
|
|
445
|
-
sticker.render();
|
|
446
|
-
return;
|
|
447
|
-
}
|
|
448
|
-
if (dataString.includes("ready in")) {
|
|
449
|
-
console.log("");
|
|
450
|
-
console.log(dataString.trim());
|
|
451
|
-
return;
|
|
452
|
-
}
|
|
453
|
-
lines.forEach((line) => {
|
|
454
|
-
if (line.trim()) {
|
|
455
|
-
console.log(line);
|
|
456
|
-
}
|
|
457
|
-
});
|
|
458
|
-
}
|
|
360
|
+
#scannedTypeScriptFiles = /* @__PURE__ */ new Set();
|
|
459
361
|
/**
|
|
460
|
-
*
|
|
362
|
+
* Picomatch matcher function to know if a file path is
|
|
363
|
+
* part of includes
|
|
461
364
|
*/
|
|
462
|
-
#
|
|
463
|
-
const dataString = data.toString();
|
|
464
|
-
const lines = dataString.split("\n");
|
|
465
|
-
lines.forEach((line) => {
|
|
466
|
-
if (line.trim()) {
|
|
467
|
-
console.log(line);
|
|
468
|
-
}
|
|
469
|
-
});
|
|
470
|
-
}
|
|
365
|
+
#isIncluded;
|
|
471
366
|
/**
|
|
472
|
-
*
|
|
367
|
+
* Picomatch matcher function to know if a file path is
|
|
368
|
+
* part of excludes
|
|
473
369
|
*/
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
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;
|
|
477
397
|
}
|
|
478
398
|
/**
|
|
479
|
-
*
|
|
480
|
-
*
|
|
481
|
-
*
|
|
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.
|
|
482
457
|
*/
|
|
483
|
-
|
|
484
|
-
if (
|
|
485
|
-
|
|
458
|
+
shouldWatchDirectory = memoize((absolutePath) => {
|
|
459
|
+
if (absolutePath === this.#cwd) {
|
|
460
|
+
debug_default("watching project root");
|
|
461
|
+
return true;
|
|
486
462
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
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
|
|
504
486
|
}
|
|
505
|
-
|
|
506
|
-
this.#
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
this.#logAssetsDevServerMessage(data);
|
|
487
|
+
);
|
|
488
|
+
this.#isMetaFileWithReloadsDisabled = picomatch(
|
|
489
|
+
metaFiles.filter((file) => !file.reloadServer).map((file) => file.pattern),
|
|
490
|
+
{
|
|
491
|
+
cwd: this.#cwd
|
|
511
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
|
|
512
502
|
});
|
|
513
|
-
this.#
|
|
514
|
-
this.#
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
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
|
|
520
513
|
});
|
|
521
514
|
}
|
|
522
515
|
/**
|
|
523
|
-
*
|
|
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.
|
|
524
521
|
*/
|
|
525
|
-
|
|
526
|
-
if (
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
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;
|
|
530
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;
|
|
531
559
|
}
|
|
532
560
|
};
|
|
533
561
|
|
|
534
562
|
// src/dev_server.ts
|
|
535
|
-
var ui3 = cliui3();
|
|
536
563
|
var DevServer = class {
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
* Flag to know if the dev server is running in watch
|
|
542
|
-
* mode
|
|
543
|
-
*/
|
|
544
|
-
#isWatching = false;
|
|
564
|
+
constructor(cwd, options) {
|
|
565
|
+
this.cwd = cwd;
|
|
566
|
+
this.options = options;
|
|
567
|
+
}
|
|
545
568
|
/**
|
|
546
|
-
*
|
|
569
|
+
* External listeners that are invoked when child process
|
|
570
|
+
* gets an error or closes
|
|
547
571
|
*/
|
|
548
|
-
#
|
|
572
|
+
#onError;
|
|
573
|
+
#onClose;
|
|
549
574
|
/**
|
|
550
|
-
*
|
|
551
|
-
*
|
|
575
|
+
* The stickyPort is set by the start and the startAndWatch methods
|
|
576
|
+
* and we will continue to use that port during restart
|
|
552
577
|
*/
|
|
553
|
-
#
|
|
578
|
+
#stickyPort;
|
|
554
579
|
/**
|
|
555
|
-
*
|
|
556
|
-
* meta file with reloadServer option disabled
|
|
580
|
+
* The mode is set by the start and the startAndWatch methods
|
|
557
581
|
*/
|
|
558
|
-
#
|
|
582
|
+
#mode = "static";
|
|
559
583
|
/**
|
|
560
|
-
*
|
|
561
|
-
* gets an error or closes
|
|
584
|
+
* Reference to chokidar watcher
|
|
562
585
|
*/
|
|
563
|
-
#
|
|
564
|
-
#onClose;
|
|
586
|
+
#watcher;
|
|
565
587
|
/**
|
|
566
588
|
* Reference to the child process
|
|
567
589
|
*/
|
|
568
590
|
#httpServer;
|
|
569
591
|
/**
|
|
570
|
-
*
|
|
592
|
+
* Filesystem is used to decide which files to watch or entertain when
|
|
593
|
+
* using hot-hook
|
|
571
594
|
*/
|
|
572
|
-
#
|
|
573
|
-
/**
|
|
574
|
-
* Reference to the assets server
|
|
575
|
-
*/
|
|
576
|
-
#assetsServer;
|
|
595
|
+
#fileSystem;
|
|
577
596
|
/**
|
|
578
597
|
* Hooks to execute custom actions during the dev server lifecycle
|
|
579
598
|
*/
|
|
580
599
|
#hooks;
|
|
581
600
|
/**
|
|
582
|
-
*
|
|
601
|
+
* Restarts the HTTP server and throttle concurrent calls to
|
|
602
|
+
* ensure we do not end up with a long loop of restarts
|
|
583
603
|
*/
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
this.#cwd = cwd;
|
|
589
|
-
this.#options = options;
|
|
590
|
-
this.#hooks = new AssemblerHooks(options.hooks);
|
|
591
|
-
if (this.#options.hmr) {
|
|
592
|
-
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");
|
|
593
608
|
}
|
|
594
|
-
this.#
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
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;
|
|
600
620
|
}
|
|
621
|
+
/**
|
|
622
|
+
* Script file to start the development server
|
|
623
|
+
*/
|
|
624
|
+
scriptFile = "bin/server.ts";
|
|
601
625
|
/**
|
|
602
626
|
* Inspect if child process message is from AdonisJS HTTP server
|
|
603
627
|
*/
|
|
@@ -605,7 +629,25 @@ var DevServer = class {
|
|
|
605
629
|
return message !== null && typeof message === "object" && "isAdonisJS" in message && "environment" in message && message.environment === "web";
|
|
606
630
|
}
|
|
607
631
|
/**
|
|
608
|
-
*
|
|
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
|
|
609
651
|
*/
|
|
610
652
|
#isHotHookMessage(message) {
|
|
611
653
|
return message !== null && typeof message === "object" && "type" in message && typeof message.type === "string" && message.type.startsWith("hot-hook:");
|
|
@@ -614,121 +656,96 @@ var DevServer = class {
|
|
|
614
656
|
* Conditionally clear the terminal screen
|
|
615
657
|
*/
|
|
616
658
|
#clearScreen() {
|
|
617
|
-
if (this
|
|
659
|
+
if (this.options.clearScreen) {
|
|
618
660
|
process.stdout.write("\x1Bc");
|
|
619
661
|
}
|
|
620
662
|
}
|
|
621
663
|
/**
|
|
622
|
-
*
|
|
623
|
-
*/
|
|
624
|
-
#startHTTPServer(port, mode) {
|
|
625
|
-
const hooksArgs = { colors: this.#colors, logger: this.#logger };
|
|
626
|
-
this.#httpServer = runNode(this.#cwd, {
|
|
627
|
-
script: this.#scriptFile,
|
|
628
|
-
env: { PORT: port, ...this.#options.env },
|
|
629
|
-
nodeArgs: this.#options.nodeArgs,
|
|
630
|
-
reject: true,
|
|
631
|
-
scriptArgs: this.#options.scriptArgs
|
|
632
|
-
});
|
|
633
|
-
this.#httpServer.on("message", async (message) => {
|
|
634
|
-
if (this.#isHotHookMessage(message)) {
|
|
635
|
-
const path = relative3(fileURLToPath3(this.#cwd), message.path || message.paths?.[0]);
|
|
636
|
-
this.#hooks.onSourceFileChanged(hooksArgs, path);
|
|
637
|
-
if (message.type === "hot-hook:full-reload") {
|
|
638
|
-
this.#clearScreen();
|
|
639
|
-
this.#logger.log(`${this.#colors.green("full-reload")} ${path}`);
|
|
640
|
-
this.#restartHTTPServer(port);
|
|
641
|
-
this.#hooks.onDevServerStarted(hooksArgs);
|
|
642
|
-
} else if (message.type === "hot-hook:invalidated") {
|
|
643
|
-
this.#logger.log(`${this.#colors.green("invalidated")} ${path}`);
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
if (this.#isAdonisJSReadyMessage(message)) {
|
|
647
|
-
const host = message.host === "0.0.0.0" ? "127.0.0.1" : message.host;
|
|
648
|
-
const displayMessage = ui3.sticker().useColors(this.#colors).useRenderer(this.#logger.getRenderer()).add(`Server address: ${this.#colors.cyan(`http://${host}:${message.port}`)}`);
|
|
649
|
-
const watchMode = this.#options.hmr ? "HMR" : this.#isWatching ? "Legacy" : "None";
|
|
650
|
-
displayMessage.add(`Watch Mode: ${this.#colors.cyan(watchMode)}`);
|
|
651
|
-
if (message.duration) {
|
|
652
|
-
displayMessage.add(`Ready in: ${this.#colors.cyan(prettyHrtime(message.duration))}`);
|
|
653
|
-
}
|
|
654
|
-
displayMessage.render();
|
|
655
|
-
await this.#hooks.onDevServerStarted({ colors: ui3.colors, logger: this.#logger });
|
|
656
|
-
}
|
|
657
|
-
});
|
|
658
|
-
this.#httpServer.then((result) => {
|
|
659
|
-
if (mode === "nonblocking") {
|
|
660
|
-
this.#onClose?.(result.exitCode);
|
|
661
|
-
this.#watcher?.close();
|
|
662
|
-
this.#assetsServer?.stop();
|
|
663
|
-
} else {
|
|
664
|
-
this.#logger.info("Underlying HTTP server closed. Still watching for changes");
|
|
665
|
-
}
|
|
666
|
-
}).catch((error) => {
|
|
667
|
-
if (mode === "nonblocking") {
|
|
668
|
-
this.#onError?.(error);
|
|
669
|
-
this.#watcher?.close();
|
|
670
|
-
this.#assetsServer?.stop();
|
|
671
|
-
} else {
|
|
672
|
-
this.#logger.info("Underlying HTTP server died. Still watching for changes");
|
|
673
|
-
}
|
|
674
|
-
});
|
|
675
|
-
}
|
|
676
|
-
/**
|
|
677
|
-
* Starts the assets server
|
|
664
|
+
* Handles file change event
|
|
678
665
|
*/
|
|
679
|
-
#
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
this.#assetsServer.start();
|
|
683
|
-
}
|
|
684
|
-
/**
|
|
685
|
-
* Restarts the HTTP server in the watch mode. Do not call this
|
|
686
|
-
* method when not in watch mode
|
|
687
|
-
*/
|
|
688
|
-
#restartHTTPServer(port) {
|
|
689
|
-
if (this.#httpServer) {
|
|
690
|
-
this.#httpServer.removeAllListeners();
|
|
691
|
-
this.#httpServer.kill("SIGKILL");
|
|
692
|
-
}
|
|
693
|
-
this.#startHTTPServer(port, "blocking");
|
|
694
|
-
}
|
|
695
|
-
/**
|
|
696
|
-
* Handles a non TypeScript file change
|
|
697
|
-
*/
|
|
698
|
-
#handleFileChange(action, port, relativePath) {
|
|
699
|
-
if (isDotEnvFile(relativePath)) {
|
|
700
|
-
this.#clearScreen();
|
|
701
|
-
this.#logger.log(`${this.#colors.green(action)} ${relativePath}`);
|
|
702
|
-
this.#restartHTTPServer(port);
|
|
666
|
+
#handleFileChange(filePath, action, hotReplaced) {
|
|
667
|
+
const file = this.#fileSystem.inspect(filePath);
|
|
668
|
+
if (!file) {
|
|
703
669
|
return;
|
|
704
670
|
}
|
|
705
|
-
if (
|
|
706
|
-
this
|
|
707
|
-
this.#logger.log(`${this.#colors.green(action)} ${relativePath}`);
|
|
708
|
-
this.#restartHTTPServer(port);
|
|
671
|
+
if (hotReplaced) {
|
|
672
|
+
this.ui.logger.log(`${this.ui.colors.green("invalidated")} ${filePath}`);
|
|
709
673
|
return;
|
|
710
674
|
}
|
|
711
|
-
if (
|
|
675
|
+
if (file.reloadServer) {
|
|
712
676
|
this.#clearScreen();
|
|
713
|
-
this
|
|
677
|
+
this.ui.logger.log(`${this.ui.colors.green(action)} ${filePath}`);
|
|
678
|
+
this.#restartHTTPServer();
|
|
679
|
+
return;
|
|
714
680
|
}
|
|
681
|
+
this.ui.logger.log(`${this.ui.colors.green(action)} ${filePath}`);
|
|
715
682
|
}
|
|
716
683
|
/**
|
|
717
|
-
*
|
|
684
|
+
* Registers inline hooks for the file changes and restarts the
|
|
685
|
+
* HTTP server when a file gets changed.
|
|
718
686
|
*/
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
this.#
|
|
722
|
-
|
|
723
|
-
|
|
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"));
|
|
724
694
|
}
|
|
725
695
|
/**
|
|
726
|
-
*
|
|
696
|
+
* Starts the HTTP server
|
|
727
697
|
*/
|
|
728
|
-
|
|
729
|
-
this.#
|
|
730
|
-
this
|
|
731
|
-
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
|
+
});
|
|
732
749
|
}
|
|
733
750
|
/**
|
|
734
751
|
* Add listener to get notified when dev server is
|
|
@@ -747,11 +764,10 @@ var DevServer = class {
|
|
|
747
764
|
return this;
|
|
748
765
|
}
|
|
749
766
|
/**
|
|
750
|
-
* Close watchers and running child
|
|
767
|
+
* Close watchers and the running child process
|
|
751
768
|
*/
|
|
752
769
|
async close() {
|
|
753
770
|
await this.#watcher?.close();
|
|
754
|
-
this.#assetsServer?.stop();
|
|
755
771
|
if (this.#httpServer) {
|
|
756
772
|
this.#httpServer.removeAllListeners();
|
|
757
773
|
this.#httpServer.kill("SIGKILL");
|
|
@@ -760,173 +776,175 @@ var DevServer = class {
|
|
|
760
776
|
/**
|
|
761
777
|
* Start the development server
|
|
762
778
|
*/
|
|
763
|
-
async start() {
|
|
764
|
-
|
|
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
|
+
}
|
|
765
804
|
this.#clearScreen();
|
|
766
|
-
this
|
|
767
|
-
this.#startHTTPServer(
|
|
768
|
-
this.#startAssetsServer();
|
|
805
|
+
this.ui.logger.info("starting HTTP server...");
|
|
806
|
+
await this.#startHTTPServer(this.#stickyPort);
|
|
769
807
|
}
|
|
770
808
|
/**
|
|
771
809
|
* Start the development server in watch mode
|
|
772
810
|
*/
|
|
773
811
|
async startAndWatch(ts, options) {
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
this.#isWatching = true;
|
|
777
|
-
this.#clearScreen();
|
|
778
|
-
this.#logger.info("starting HTTP server...");
|
|
779
|
-
this.#startHTTPServer(port, "blocking");
|
|
780
|
-
this.#startAssetsServer();
|
|
781
|
-
const output = watch(this.#cwd, ts, options || {});
|
|
782
|
-
if (!output) {
|
|
783
|
-
this.#onClose?.(1);
|
|
812
|
+
const tsConfig = parseConfig(this.cwd, ts);
|
|
813
|
+
if (!tsConfig) {
|
|
784
814
|
return;
|
|
785
815
|
}
|
|
786
|
-
this.#
|
|
787
|
-
|
|
788
|
-
|
|
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...");
|
|
789
846
|
});
|
|
790
|
-
|
|
791
|
-
this
|
|
792
|
-
this
|
|
847
|
+
this.#watcher.on("error", (error) => {
|
|
848
|
+
this.ui.logger.warning("file system watcher failure");
|
|
849
|
+
this.ui.logger.fatal(error);
|
|
793
850
|
this.#onError?.(error);
|
|
794
|
-
|
|
851
|
+
this.#watcher?.close();
|
|
795
852
|
});
|
|
796
|
-
|
|
797
|
-
"source:add",
|
|
798
|
-
({ relativePath }) => this.#handleSourceFileChange("add", port, relativePath)
|
|
799
|
-
);
|
|
800
|
-
output.watcher.on(
|
|
801
|
-
"source:change",
|
|
802
|
-
({ relativePath }) => this.#handleSourceFileChange("update", port, relativePath)
|
|
803
|
-
);
|
|
804
|
-
output.watcher.on(
|
|
805
|
-
"source:unlink",
|
|
806
|
-
({ relativePath }) => this.#handleSourceFileChange("delete", port, relativePath)
|
|
807
|
-
);
|
|
808
|
-
output.watcher.on(
|
|
853
|
+
this.#watcher.on(
|
|
809
854
|
"add",
|
|
810
|
-
(
|
|
855
|
+
(filePath) => this.#hooks.runner("fileAdded").run(string3.toUnixSlash(filePath), this)
|
|
811
856
|
);
|
|
812
|
-
|
|
857
|
+
this.#watcher.on(
|
|
813
858
|
"change",
|
|
814
|
-
(
|
|
859
|
+
(filePath) => this.#hooks.runner("fileChanged").run(string3.toUnixSlash(filePath), false, this)
|
|
815
860
|
);
|
|
816
|
-
|
|
861
|
+
this.#watcher.on(
|
|
817
862
|
"unlink",
|
|
818
|
-
(
|
|
863
|
+
(filePath) => this.#hooks.runner("fileRemoved").run(string3.toUnixSlash(filePath), this)
|
|
819
864
|
);
|
|
820
865
|
}
|
|
821
866
|
};
|
|
822
867
|
|
|
823
868
|
// src/test_runner.ts
|
|
824
|
-
import
|
|
825
|
-
import {
|
|
826
|
-
|
|
869
|
+
import { cliui as cliui3 } from "@poppinss/cliui";
|
|
870
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
871
|
+
import string4 from "@poppinss/utils/string";
|
|
827
872
|
var TestRunner = class {
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
* The script file to run as a child process
|
|
833
|
-
*/
|
|
834
|
-
#scriptFile = "bin/test.js";
|
|
873
|
+
constructor(cwd, options) {
|
|
874
|
+
this.cwd = cwd;
|
|
875
|
+
this.options = options;
|
|
876
|
+
}
|
|
835
877
|
/**
|
|
836
|
-
*
|
|
837
|
-
*
|
|
878
|
+
* External listeners that are invoked when child process
|
|
879
|
+
* gets an error or closes
|
|
838
880
|
*/
|
|
839
|
-
#
|
|
881
|
+
#onError;
|
|
882
|
+
#onClose;
|
|
840
883
|
/**
|
|
841
|
-
*
|
|
842
|
-
*
|
|
884
|
+
* The stickyPort is set by the startAndWatch method and we will
|
|
885
|
+
* continue to use this port during re-runs
|
|
843
886
|
*/
|
|
844
|
-
#
|
|
887
|
+
#stickyPort;
|
|
845
888
|
/**
|
|
846
|
-
*
|
|
889
|
+
* Reference to chokidar watcher
|
|
847
890
|
*/
|
|
848
|
-
#
|
|
891
|
+
#watcher;
|
|
849
892
|
/**
|
|
850
|
-
*
|
|
851
|
-
* command. In watch mode, we will append an additional
|
|
852
|
-
* filter to run tests only for the file that has been
|
|
853
|
-
* changed.
|
|
893
|
+
* Reference to the test script child process
|
|
854
894
|
*/
|
|
855
|
-
#
|
|
895
|
+
#testsProcess;
|
|
856
896
|
/**
|
|
857
|
-
*
|
|
858
|
-
*
|
|
859
|
-
* we use this flag to know if we are already busy in running
|
|
860
|
-
* tests and ignore file-changes.
|
|
897
|
+
* Filesystem is used to decide which files to watch or entertain in watch
|
|
898
|
+
* mode
|
|
861
899
|
*/
|
|
862
|
-
#
|
|
900
|
+
#fileSystem;
|
|
863
901
|
/**
|
|
864
|
-
*
|
|
865
|
-
* gets an error or closes
|
|
902
|
+
* Hooks to execute custom actions during the tests runner lifecycle
|
|
866
903
|
*/
|
|
867
|
-
#
|
|
868
|
-
#onClose;
|
|
904
|
+
#hooks;
|
|
869
905
|
/**
|
|
870
|
-
*
|
|
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
|
|
871
908
|
*/
|
|
872
|
-
#
|
|
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");
|
|
873
916
|
/**
|
|
874
|
-
*
|
|
917
|
+
* CLI UI to log colorful messages
|
|
875
918
|
*/
|
|
876
|
-
|
|
919
|
+
ui = cliui3();
|
|
877
920
|
/**
|
|
878
|
-
*
|
|
921
|
+
* The script file to run as a child process
|
|
879
922
|
*/
|
|
880
|
-
|
|
881
|
-
/**
|
|
882
|
-
* Getting reference to colors library from logger
|
|
883
|
-
*/
|
|
884
|
-
get #colors() {
|
|
885
|
-
return this.#logger.getColors();
|
|
886
|
-
}
|
|
887
|
-
constructor(cwd, options) {
|
|
888
|
-
this.#cwd = cwd;
|
|
889
|
-
this.#options = options;
|
|
890
|
-
this.#isMetaFile = picomatch2((this.#options.metaFiles || []).map(({ pattern }) => pattern));
|
|
891
|
-
this.#isTestFile = picomatch2(
|
|
892
|
-
this.#options.suites.filter((suite) => {
|
|
893
|
-
if (this.#options.filters.suites) {
|
|
894
|
-
return this.#options.filters.suites.includes(suite.name);
|
|
895
|
-
}
|
|
896
|
-
return true;
|
|
897
|
-
}).map((suite) => suite.files).flat(1)
|
|
898
|
-
);
|
|
899
|
-
this.#scriptArgs = this.#convertOptionsToArgs().concat(this.#options.scriptArgs);
|
|
900
|
-
this.#initialFiltersArgs = this.#convertFiltersToArgs(this.#options.filters);
|
|
901
|
-
}
|
|
923
|
+
scriptFile = "bin/test.ts";
|
|
902
924
|
/**
|
|
903
925
|
* Convert test runner options to the CLI args
|
|
904
926
|
*/
|
|
905
927
|
#convertOptionsToArgs() {
|
|
906
928
|
const args = [];
|
|
907
|
-
if (this
|
|
929
|
+
if (this.options.reporters) {
|
|
908
930
|
args.push("--reporters");
|
|
909
|
-
args.push(this
|
|
931
|
+
args.push(this.options.reporters.join(","));
|
|
910
932
|
}
|
|
911
|
-
if (this
|
|
933
|
+
if (this.options.timeout !== void 0) {
|
|
912
934
|
args.push("--timeout");
|
|
913
|
-
args.push(String(this
|
|
935
|
+
args.push(String(this.options.timeout));
|
|
914
936
|
}
|
|
915
|
-
if (this
|
|
937
|
+
if (this.options.failed) {
|
|
916
938
|
args.push("--failed");
|
|
917
939
|
}
|
|
918
|
-
if (this
|
|
940
|
+
if (this.options.retries !== void 0) {
|
|
919
941
|
args.push("--retries");
|
|
920
|
-
args.push(String(this
|
|
942
|
+
args.push(String(this.options.retries));
|
|
921
943
|
}
|
|
922
944
|
return args;
|
|
923
945
|
}
|
|
924
946
|
/**
|
|
925
947
|
* Converts all known filters to CLI args.
|
|
926
|
-
*
|
|
927
|
-
* The following code snippet may seem like repetitive code. But, it
|
|
928
|
-
* is done intentionally to have visibility around how each filter
|
|
929
|
-
* is converted to an arg.
|
|
930
948
|
*/
|
|
931
949
|
#convertFiltersToArgs(filters) {
|
|
932
950
|
const args = [];
|
|
@@ -955,94 +973,74 @@ var TestRunner = class {
|
|
|
955
973
|
* Conditionally clear the terminal screen
|
|
956
974
|
*/
|
|
957
975
|
#clearScreen() {
|
|
958
|
-
if (this
|
|
976
|
+
if (this.options.clearScreen) {
|
|
959
977
|
process.stdout.write("\x1Bc");
|
|
960
978
|
}
|
|
961
979
|
}
|
|
962
980
|
/**
|
|
963
981
|
* Runs tests
|
|
964
982
|
*/
|
|
965
|
-
#runTests(port,
|
|
966
|
-
this.#
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
this.
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
this.
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
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());
|
|
987
1018
|
});
|
|
988
1019
|
}
|
|
989
1020
|
/**
|
|
990
|
-
*
|
|
991
|
-
* executed in watch mode only.
|
|
992
|
-
*/
|
|
993
|
-
#rerunTests(port, filters) {
|
|
994
|
-
if (this.#testScript) {
|
|
995
|
-
this.#testScript.removeAllListeners();
|
|
996
|
-
this.#testScript.kill("SIGKILL");
|
|
997
|
-
}
|
|
998
|
-
this.#runTests(port, "blocking", filters);
|
|
999
|
-
}
|
|
1000
|
-
/**
|
|
1001
|
-
* Starts the assets server
|
|
1002
|
-
*/
|
|
1003
|
-
#startAssetsServer() {
|
|
1004
|
-
this.#assetsServer = new AssetsDevServer(this.#cwd, this.#options.assets);
|
|
1005
|
-
this.#assetsServer.setLogger(this.#logger);
|
|
1006
|
-
this.#assetsServer.start();
|
|
1007
|
-
}
|
|
1008
|
-
/**
|
|
1009
|
-
* Handles a non TypeScript file change
|
|
1010
|
-
*/
|
|
1011
|
-
#handleFileChange(action, port, relativePath) {
|
|
1012
|
-
if (this.#isBusy) {
|
|
1013
|
-
return;
|
|
1014
|
-
}
|
|
1015
|
-
if (isDotEnvFile(relativePath) || this.#isMetaFile(relativePath)) {
|
|
1016
|
-
this.#clearScreen();
|
|
1017
|
-
this.#logger.log(`${this.#colors.green(action)} ${relativePath}`);
|
|
1018
|
-
this.#rerunTests(port);
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
/**
|
|
1022
|
-
* Handles TypeScript source file change
|
|
1021
|
+
* Handles file change event
|
|
1023
1022
|
*/
|
|
1024
|
-
#
|
|
1025
|
-
|
|
1023
|
+
#handleFileChange(filePath, action) {
|
|
1024
|
+
const file = this.#fileSystem.inspect(filePath);
|
|
1025
|
+
if (!file) {
|
|
1026
1026
|
return;
|
|
1027
1027
|
}
|
|
1028
1028
|
this.#clearScreen();
|
|
1029
|
-
this
|
|
1030
|
-
if (
|
|
1031
|
-
this.#
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
});
|
|
1035
|
-
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();
|
|
1036
1034
|
}
|
|
1037
|
-
this.#rerunTests(port);
|
|
1038
1035
|
}
|
|
1039
1036
|
/**
|
|
1040
|
-
*
|
|
1037
|
+
* Registers inline hooks for the file changes and restarts the
|
|
1038
|
+
* HTTP server when a file gets changed.
|
|
1041
1039
|
*/
|
|
1042
|
-
|
|
1043
|
-
this.#
|
|
1044
|
-
this.#
|
|
1045
|
-
|
|
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"));
|
|
1046
1044
|
}
|
|
1047
1045
|
/**
|
|
1048
1046
|
* Add listener to get notified when dev server is
|
|
@@ -1065,69 +1063,90 @@ var TestRunner = class {
|
|
|
1065
1063
|
*/
|
|
1066
1064
|
async close() {
|
|
1067
1065
|
await this.#watcher?.close();
|
|
1068
|
-
this.#
|
|
1069
|
-
|
|
1070
|
-
this.#
|
|
1071
|
-
this.#testScript.kill("SIGKILL");
|
|
1066
|
+
if (this.#testsProcess) {
|
|
1067
|
+
this.#testsProcess.removeAllListeners();
|
|
1068
|
+
this.#testsProcess.kill("SIGKILL");
|
|
1072
1069
|
}
|
|
1073
1070
|
}
|
|
1074
1071
|
/**
|
|
1075
1072
|
* Runs tests
|
|
1076
1073
|
*/
|
|
1077
1074
|
async run() {
|
|
1078
|
-
|
|
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
|
+
]);
|
|
1079
1083
|
this.#clearScreen();
|
|
1080
|
-
this
|
|
1081
|
-
this.#
|
|
1082
|
-
this.#runTests(port, "nonblocking");
|
|
1084
|
+
this.ui.logger.info("booting application to run tests...");
|
|
1085
|
+
await this.#runTests(this.#stickyPort);
|
|
1083
1086
|
}
|
|
1084
1087
|
/**
|
|
1085
1088
|
* Run tests in watch mode
|
|
1086
1089
|
*/
|
|
1087
1090
|
async runAndWatch(ts, options) {
|
|
1088
|
-
const
|
|
1089
|
-
|
|
1090
|
-
this.#startAssetsServer();
|
|
1091
|
-
this.#logger.info("booting application to run tests...");
|
|
1092
|
-
this.#runTests(port, "blocking");
|
|
1093
|
-
const output = watch(this.#cwd, ts, options || {});
|
|
1094
|
-
if (!output) {
|
|
1095
|
-
this.#onClose?.(1);
|
|
1091
|
+
const tsConfig = parseConfig(this.cwd, ts);
|
|
1092
|
+
if (!tsConfig) {
|
|
1096
1093
|
return;
|
|
1097
1094
|
}
|
|
1098
|
-
this.#
|
|
1099
|
-
|
|
1100
|
-
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
|
+
})
|
|
1101
1104
|
});
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
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);
|
|
1105
1136
|
this.#onError?.(error);
|
|
1106
|
-
|
|
1137
|
+
this.#watcher?.close();
|
|
1107
1138
|
});
|
|
1108
|
-
|
|
1109
|
-
"source:add",
|
|
1110
|
-
({ relativePath }) => this.#handleSourceFileChange("add", port, relativePath)
|
|
1111
|
-
);
|
|
1112
|
-
output.watcher.on(
|
|
1113
|
-
"source:change",
|
|
1114
|
-
({ relativePath }) => this.#handleSourceFileChange("update", port, relativePath)
|
|
1115
|
-
);
|
|
1116
|
-
output.watcher.on(
|
|
1117
|
-
"source:unlink",
|
|
1118
|
-
({ relativePath }) => this.#handleSourceFileChange("delete", port, relativePath)
|
|
1119
|
-
);
|
|
1120
|
-
output.watcher.on(
|
|
1139
|
+
this.#watcher.on(
|
|
1121
1140
|
"add",
|
|
1122
|
-
(
|
|
1141
|
+
(filePath) => this.#hooks.runner("fileAdded").run(string4.toUnixSlash(filePath), this)
|
|
1123
1142
|
);
|
|
1124
|
-
|
|
1143
|
+
this.#watcher.on(
|
|
1125
1144
|
"change",
|
|
1126
|
-
(
|
|
1145
|
+
(filePath) => this.#hooks.runner("fileChanged").run(string4.toUnixSlash(filePath), false, this)
|
|
1127
1146
|
);
|
|
1128
|
-
|
|
1147
|
+
this.#watcher.on(
|
|
1129
1148
|
"unlink",
|
|
1130
|
-
(
|
|
1149
|
+
(filePath) => this.#hooks.runner("fileRemoved").run(string4.toUnixSlash(filePath), this)
|
|
1131
1150
|
);
|
|
1132
1151
|
}
|
|
1133
1152
|
};
|
|
@@ -1136,4 +1155,3 @@ export {
|
|
|
1136
1155
|
DevServer,
|
|
1137
1156
|
TestRunner
|
|
1138
1157
|
};
|
|
1139
|
-
//# sourceMappingURL=index.js.map
|