@adonisjs/assembler 6.1.3-23 → 6.1.3-25

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.
@@ -1,183 +0,0 @@
1
- /*
2
- * @adonisjs/assembler
3
- *
4
- * (c) AdonisJS
5
- *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
8
- */
9
- import cpy from 'cpy';
10
- import { isNotJunk } from 'junk';
11
- import fastGlob from 'fast-glob';
12
- import getRandomPort from 'get-port';
13
- import { fileURLToPath } from 'node:url';
14
- import { execaNode, execa } from 'execa';
15
- import { isAbsolute, relative } from 'node:path';
16
- import { EnvLoader, EnvParser } from '@adonisjs/env';
17
- import { ConfigParser, Watcher } from '@poppinss/chokidar-ts';
18
- import debug from './debug.js';
19
- /**
20
- * Default set of args to pass in order to run TypeScript
21
- * source. Used by "run" and "runNode" scripts
22
- */
23
- const DEFAULT_NODE_ARGS = [
24
- // Use ts-node/esm loader. The project must install it
25
- '--loader=ts-node/esm',
26
- // Disable annonying warnings
27
- '--no-warnings',
28
- // Enable expiremental meta resolve for cases where someone uses magic import string
29
- '--experimental-import-meta-resolve',
30
- // Enable source maps, since TSNode source maps are broken
31
- '--enable-source-maps',
32
- ];
33
- /**
34
- * Parses tsconfig.json and prints errors using typescript compiler
35
- * host
36
- */
37
- export function parseConfig(cwd, ts) {
38
- const { config, error } = new ConfigParser(cwd, 'tsconfig.json', ts).parse();
39
- if (error) {
40
- const compilerHost = ts.createCompilerHost({});
41
- console.log(ts.formatDiagnosticsWithColorAndContext([error], compilerHost));
42
- return;
43
- }
44
- if (config.errors.length) {
45
- const compilerHost = ts.createCompilerHost({});
46
- console.log(ts.formatDiagnosticsWithColorAndContext(config.errors, compilerHost));
47
- return;
48
- }
49
- return config;
50
- }
51
- /**
52
- * Runs a Node.js script as a child process and inherits the stdio streams
53
- */
54
- export function runNode(cwd, options) {
55
- const childProcess = execaNode(options.script, options.scriptArgs, {
56
- nodeOptions: DEFAULT_NODE_ARGS.concat(options.nodeArgs),
57
- preferLocal: true,
58
- windowsHide: false,
59
- localDir: cwd,
60
- cwd,
61
- buffer: false,
62
- stdio: options.stdio || 'inherit',
63
- env: {
64
- ...(options.stdio === 'pipe' ? { FORCE_COLOR: 'true' } : {}),
65
- ...options.env,
66
- },
67
- });
68
- return childProcess;
69
- }
70
- /**
71
- * Runs a script as a child process and inherits the stdio streams
72
- */
73
- export function run(cwd, options) {
74
- const childProcess = execa(options.script, options.scriptArgs, {
75
- preferLocal: true,
76
- windowsHide: false,
77
- localDir: cwd,
78
- cwd,
79
- buffer: false,
80
- stdio: options.stdio || 'inherit',
81
- env: {
82
- ...(options.stdio === 'pipe' ? { FORCE_COLOR: 'true' } : {}),
83
- ...options.env,
84
- },
85
- });
86
- return childProcess;
87
- }
88
- /**
89
- * Watches the file system using tsconfig file
90
- */
91
- export function watch(cwd, ts, options) {
92
- const config = parseConfig(cwd, ts);
93
- if (!config) {
94
- return;
95
- }
96
- const watcher = new Watcher(typeof cwd === 'string' ? cwd : fileURLToPath(cwd), config);
97
- const chokidar = watcher.watch(['.'], { usePolling: options.poll });
98
- return { watcher, chokidar };
99
- }
100
- /**
101
- * Check if file is an .env file
102
- */
103
- export function isDotEnvFile(filePath) {
104
- if (filePath === '.env') {
105
- return true;
106
- }
107
- return filePath.includes('.env.');
108
- }
109
- /**
110
- * Check if file is .adonisrc.json file
111
- */
112
- export function isRcFile(filePath) {
113
- return filePath === '.adonisrc.json';
114
- }
115
- /**
116
- * Returns the port to use after inspect the dot-env files inside
117
- * a given directory.
118
- *
119
- * A random port is used when the specified port is in use. Following
120
- * is the logic for finding a specified port.
121
- *
122
- * - The "process.env.PORT" value is used if exists.
123
- * - The dot-env files are loaded using the "EnvLoader" and the PORT
124
- * value is by iterating over all the loaded files. The iteration
125
- * stops after first find.
126
- */
127
- export async function getPort(cwd) {
128
- /**
129
- * Use existing port if exists
130
- */
131
- if (process.env.PORT) {
132
- return getRandomPort({ port: Number(process.env.PORT) });
133
- }
134
- /**
135
- * Loop over files and use the port from their contents. Stops
136
- * after first match
137
- */
138
- const files = await new EnvLoader(cwd).load();
139
- for (let file of files) {
140
- const envVariables = new EnvParser(file.contents).parse();
141
- if (envVariables.PORT) {
142
- return getRandomPort({ port: Number(envVariables.PORT) });
143
- }
144
- }
145
- /**
146
- * Use 3333 as the port
147
- */
148
- return getRandomPort({ port: 3333 });
149
- }
150
- /**
151
- * Helper function to copy files from relative paths or glob
152
- * patterns
153
- */
154
- export async function copyFiles(files, cwd, outDir) {
155
- /**
156
- * Looping over files and create a new collection with paths
157
- * and glob patterns
158
- */
159
- const { paths, patterns } = files.reduce((result, file) => {
160
- if (fastGlob.isDynamicPattern(file)) {
161
- result.patterns.push(file);
162
- }
163
- else {
164
- result.paths.push(file);
165
- }
166
- return result;
167
- }, { patterns: [], paths: [] });
168
- debug('copyFiles inputs: %O, paths: %O, patterns: %O', files, paths, patterns);
169
- /**
170
- * Getting list of relative paths from glob patterns
171
- */
172
- const filePaths = paths.concat(await fastGlob(patterns, { cwd }));
173
- /**
174
- * Computing relative destination. This is because, cpy is buggy when
175
- * outDir is an absolute path.
176
- */
177
- const destination = isAbsolute(outDir) ? relative(cwd, outDir) : outDir;
178
- debug('copying files %O to destination "%s"', filePaths, destination);
179
- return cpy(filePaths.filter(isNotJunk), destination, {
180
- cwd: cwd,
181
- flat: false,
182
- });
183
- }
@@ -1,47 +0,0 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
- import type tsStatic from 'typescript';
3
- import { type Logger } from '@poppinss/cliui';
4
- import type { TestRunnerOptions } from './types.js';
5
- /**
6
- * Exposes the API to start the development. Optionally, the watch API can be
7
- * used to watch for file changes and restart the development server.
8
- *
9
- * The Dev server performs the following actions
10
- *
11
- * - Assigns a random PORT, when PORT inside .env file is in use
12
- * - Uses tsconfig.json file to collect a list of files to watch.
13
- * - Uses metaFiles from .adonisrc.json file to collect a list of files to watch.
14
- * - Restart HTTP server on every file change.
15
- */
16
- export declare class TestRunner {
17
- #private;
18
- constructor(cwd: URL, options: TestRunnerOptions);
19
- /**
20
- * Set a custom CLI UI logger
21
- */
22
- setLogger(logger: Logger): this;
23
- /**
24
- * Add listener to get notified when dev server is
25
- * closed
26
- */
27
- onClose(callback: (exitCode: number) => any): this;
28
- /**
29
- * Add listener to get notified when dev server exists
30
- * with an error
31
- */
32
- onError(callback: (error: any) => any): this;
33
- /**
34
- * Close watchers and running child processes
35
- */
36
- close(): Promise<void>;
37
- /**
38
- * Runs tests
39
- */
40
- run(): Promise<void>;
41
- /**
42
- * Run tests in watch mode
43
- */
44
- runAndWatch(ts: typeof tsStatic, options?: {
45
- poll: boolean;
46
- }): Promise<void>;
47
- }
@@ -1,310 +0,0 @@
1
- /*
2
- * @adonisjs/assembler
3
- *
4
- * (c) AdonisJS
5
- *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
8
- */
9
- import picomatch from 'picomatch';
10
- import { cliui } from '@poppinss/cliui';
11
- import { AssetsDevServer } from './assets_dev_server.js';
12
- import { getPort, isDotEnvFile, runNode, watch } from './helpers.js';
13
- /**
14
- * Instance of CLIUI
15
- */
16
- const ui = cliui();
17
- /**
18
- * Exposes the API to start the development. Optionally, the watch API can be
19
- * used to watch for file changes and restart the development server.
20
- *
21
- * The Dev server performs the following actions
22
- *
23
- * - Assigns a random PORT, when PORT inside .env file is in use
24
- * - Uses tsconfig.json file to collect a list of files to watch.
25
- * - Uses metaFiles from .adonisrc.json file to collect a list of files to watch.
26
- * - Restart HTTP server on every file change.
27
- */
28
- export class TestRunner {
29
- #cwd;
30
- #logger = ui.logger;
31
- #options;
32
- #scriptFile = 'bin/test.js';
33
- #isMetaFile;
34
- #isTestFile;
35
- #scriptArgs;
36
- #initialFiltersArgs;
37
- /**
38
- * In watch mode, after a file is changed, we wait for the current
39
- * set of tests to finish before triggering a re-run. Therefore,
40
- * we use this flag to know if we are already busy in running
41
- * tests and ignore file-changes.
42
- */
43
- #isBusy = false;
44
- #onError;
45
- #onClose;
46
- #testScript;
47
- #watcher;
48
- #assetsServer;
49
- /**
50
- * Getting reference to colors library from logger
51
- */
52
- get #colors() {
53
- return this.#logger.getColors();
54
- }
55
- constructor(cwd, options) {
56
- this.#cwd = cwd;
57
- this.#options = options;
58
- this.#isMetaFile = picomatch((this.#options.metaFiles || []).map(({ pattern }) => pattern));
59
- this.#isTestFile = picomatch(this.#options.suites
60
- .filter((suite) => {
61
- if (this.#options.filters.suites) {
62
- return this.#options.filters.suites.includes(suite.name);
63
- }
64
- return true;
65
- })
66
- .map((suite) => suite.files)
67
- .flat(1));
68
- this.#scriptArgs = this.#convertOptionsToArgs().concat(this.#options.scriptArgs);
69
- this.#initialFiltersArgs = this.#convertFiltersToArgs(this.#options.filters);
70
- }
71
- /**
72
- * Converts options to CLI args
73
- */
74
- #convertOptionsToArgs() {
75
- const args = [];
76
- if (this.#options.reporters) {
77
- args.push('--reporters');
78
- args.push(this.#options.reporters.join(','));
79
- }
80
- if (this.#options.timeout !== undefined) {
81
- args.push('--timeout');
82
- args.push(String(this.#options.timeout));
83
- }
84
- if (this.#options.failed) {
85
- args.push('--failed');
86
- }
87
- if (this.#options.retries !== undefined) {
88
- args.push('--retries');
89
- args.push(String(this.#options.retries));
90
- }
91
- return args;
92
- }
93
- /**
94
- * Converts all known filters to CLI args.
95
- *
96
- * The following code snippet may seem like repetitive code. But, it
97
- * is done intentionally to have visibility around how each filter
98
- * is converted to an arg.
99
- */
100
- #convertFiltersToArgs(filters) {
101
- const args = [];
102
- if (filters.suites) {
103
- args.push(...filters.suites);
104
- }
105
- if (filters.files) {
106
- args.push('--files');
107
- args.push(filters.files.join(','));
108
- }
109
- if (filters.groups) {
110
- args.push('--groups');
111
- args.push(filters.groups.join(','));
112
- }
113
- if (filters.tags) {
114
- args.push('--tags');
115
- args.push(filters.tags.join(','));
116
- }
117
- if (filters.tests) {
118
- args.push('--tests');
119
- args.push(filters.tests.join(','));
120
- }
121
- return args;
122
- }
123
- /**
124
- * Conditionally clear the terminal screen
125
- */
126
- #clearScreen() {
127
- if (this.#options.clearScreen) {
128
- process.stdout.write('\u001Bc');
129
- }
130
- }
131
- /**
132
- * Runs tests
133
- */
134
- #runTests(port, mode, filters) {
135
- this.#isBusy = true;
136
- const scriptArgs = filters
137
- ? this.#convertFiltersToArgs(filters).concat(this.#scriptArgs)
138
- : this.#initialFiltersArgs.concat(this.#scriptArgs);
139
- this.#testScript = runNode(this.#cwd, {
140
- script: this.#scriptFile,
141
- env: { PORT: port, ...this.#options.env },
142
- nodeArgs: this.#options.nodeArgs,
143
- scriptArgs,
144
- });
145
- this.#testScript
146
- .then((result) => {
147
- if (mode === 'nonblocking') {
148
- this.#onClose?.(result.exitCode);
149
- this.close();
150
- }
151
- })
152
- .catch((error) => {
153
- if (mode === 'nonblocking') {
154
- this.#onError?.(error);
155
- this.close();
156
- }
157
- })
158
- .finally(() => {
159
- this.#isBusy = false;
160
- });
161
- }
162
- /**
163
- * Restarts the HTTP server
164
- */
165
- #rerunTests(port, filters) {
166
- if (this.#testScript) {
167
- this.#testScript.removeAllListeners();
168
- this.#testScript.kill('SIGKILL');
169
- }
170
- this.#runTests(port, 'blocking', filters);
171
- }
172
- /**
173
- * Starts the assets server
174
- */
175
- #startAssetsServer() {
176
- this.#assetsServer = new AssetsDevServer(this.#cwd, this.#options.assets);
177
- this.#assetsServer.setLogger(this.#logger);
178
- this.#assetsServer.start();
179
- }
180
- /**
181
- * Handles a non TypeScript file change
182
- */
183
- #handleFileChange(action, port, relativePath) {
184
- if (this.#isBusy) {
185
- return;
186
- }
187
- if (isDotEnvFile(relativePath) || this.#isMetaFile(relativePath)) {
188
- this.#clearScreen();
189
- this.#logger.log(`${this.#colors.green(action)} ${relativePath}`);
190
- this.#rerunTests(port);
191
- }
192
- }
193
- /**
194
- * Handles TypeScript source file change
195
- */
196
- #handleSourceFileChange(action, port, relativePath) {
197
- if (this.#isBusy) {
198
- return;
199
- }
200
- this.#clearScreen();
201
- this.#logger.log(`${this.#colors.green(action)} ${relativePath}`);
202
- /**
203
- * If changed file is a test file after considering the initial filters,
204
- * then only run that file
205
- */
206
- if (this.#isTestFile(relativePath)) {
207
- this.#rerunTests(port, {
208
- ...this.#options.filters,
209
- files: [relativePath],
210
- });
211
- return;
212
- }
213
- this.#rerunTests(port);
214
- }
215
- /**
216
- * Set a custom CLI UI logger
217
- */
218
- setLogger(logger) {
219
- this.#logger = logger;
220
- this.#assetsServer?.setLogger(logger);
221
- return this;
222
- }
223
- /**
224
- * Add listener to get notified when dev server is
225
- * closed
226
- */
227
- onClose(callback) {
228
- this.#onClose = callback;
229
- return this;
230
- }
231
- /**
232
- * Add listener to get notified when dev server exists
233
- * with an error
234
- */
235
- onError(callback) {
236
- this.#onError = callback;
237
- return this;
238
- }
239
- /**
240
- * Close watchers and running child processes
241
- */
242
- async close() {
243
- await this.#watcher?.close();
244
- this.#assetsServer?.stop();
245
- if (this.#testScript) {
246
- this.#testScript.removeAllListeners();
247
- this.#testScript.kill('SIGKILL');
248
- }
249
- }
250
- /**
251
- * Runs tests
252
- */
253
- async run() {
254
- const port = String(await getPort(this.#cwd));
255
- this.#clearScreen();
256
- this.#startAssetsServer();
257
- this.#logger.info('booting application to run tests...');
258
- this.#runTests(port, 'nonblocking');
259
- }
260
- /**
261
- * Run tests in watch mode
262
- */
263
- async runAndWatch(ts, options) {
264
- const port = String(await getPort(this.#cwd));
265
- this.#clearScreen();
266
- this.#startAssetsServer();
267
- this.#logger.info('booting application to run tests...');
268
- this.#runTests(port, 'blocking');
269
- /**
270
- * Create watcher using tsconfig.json file
271
- */
272
- const output = watch(this.#cwd, ts, options || {});
273
- if (!output) {
274
- this.#onClose?.(1);
275
- return;
276
- }
277
- /**
278
- * Storing reference to watcher, so that we can close it
279
- * when HTTP server exists with error
280
- */
281
- this.#watcher = output.chokidar;
282
- /**
283
- * Notify the watcher is ready
284
- */
285
- output.watcher.on('watcher:ready', () => {
286
- this.#logger.info('watching file system for changes...');
287
- });
288
- /**
289
- * Cleanup when watcher dies
290
- */
291
- output.chokidar.on('error', (error) => {
292
- this.#logger.warning('file system watcher failure');
293
- this.#logger.fatal(error);
294
- this.#onError?.(error);
295
- output.chokidar.close();
296
- });
297
- /**
298
- * Changes in TypeScript source file
299
- */
300
- output.watcher.on('source:add', ({ relativePath }) => this.#handleSourceFileChange('add', port, relativePath));
301
- output.watcher.on('source:change', ({ relativePath }) => this.#handleSourceFileChange('update', port, relativePath));
302
- output.watcher.on('source:unlink', ({ relativePath }) => this.#handleSourceFileChange('delete', port, relativePath));
303
- /**
304
- * Changes in non-TypeScript source files
305
- */
306
- output.watcher.on('add', ({ relativePath }) => this.#handleFileChange('add', port, relativePath));
307
- output.watcher.on('change', ({ relativePath }) => this.#handleFileChange('update', port, relativePath));
308
- output.watcher.on('unlink', ({ relativePath }) => this.#handleFileChange('delete', port, relativePath));
309
- }
310
- }