@adonisjs/assembler 6.1.3-2 → 6.1.3-21

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,23 +1,46 @@
1
- import getPort from 'get-port';
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
+ */
2
9
  import picomatch from 'picomatch';
3
10
  import { cliui } from '@poppinss/cliui';
4
- import { EnvLoader, EnvParser } from '@adonisjs/env';
5
- import { watch } from './watch.js';
6
- import { run, runNode } from './run.js';
11
+ import { AssetsDevServer } from './assets_dev_server.js';
12
+ import { getPort, isDotEnvFile, isRcFile, runNode, watch } from './helpers.js';
13
+ /**
14
+ * Instance of CLIUI
15
+ */
7
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
+ */
8
28
  export class DevServer {
9
29
  #cwd;
10
30
  #logger = ui.logger;
11
31
  #options;
12
32
  #isWatching = false;
13
33
  #scriptFile = 'bin/server.js';
14
- #httpServerProcess;
15
34
  #isMetaFileWithReloadsEnabled;
16
35
  #isMetaFileWithReloadsDisabled;
17
- #watcher;
18
- #assetsServerProcess;
19
36
  #onError;
20
37
  #onClose;
38
+ #httpServer;
39
+ #watcher;
40
+ #assetsServer;
41
+ /**
42
+ * Getting reference to colors library from logger
43
+ */
21
44
  get #colors() {
22
45
  return this.#logger.getColors();
23
46
  }
@@ -31,15 +54,9 @@ export class DevServer {
31
54
  .filter(({ reloadServer }) => reloadServer !== true)
32
55
  .map(({ pattern }) => pattern));
33
56
  }
34
- #isDotEnvFile(filePath) {
35
- if (filePath === '.env') {
36
- return true;
37
- }
38
- return filePath.includes('.env.');
39
- }
40
- #isRcFile(filePath) {
41
- return filePath === '.adonisrc.json';
42
- }
57
+ /**
58
+ * Inspect if child process message is from AdonisJS HTTP server
59
+ */
43
60
  #isAdonisJSReadyMessage(message) {
44
61
  return (message !== null &&
45
62
  typeof message === 'object' &&
@@ -47,65 +64,25 @@ export class DevServer {
47
64
  'environment' in message &&
48
65
  message.environment === 'web');
49
66
  }
67
+ /**
68
+ * Conditionally clear the terminal screen
69
+ */
50
70
  #clearScreen() {
51
71
  if (this.#options.clearScreen) {
52
72
  process.stdout.write('\u001Bc');
53
73
  }
54
74
  }
55
- #logViteDevServerMessage(data) {
56
- const dataString = data.toString();
57
- const lines = dataString.split('\n');
58
- if (dataString.includes('ready in')) {
59
- console.log('');
60
- console.log(dataString.trim());
61
- return;
62
- }
63
- if (dataString.includes('Local') && dataString.includes('Network')) {
64
- const sticker = ui.sticker().useColors(this.#colors).useRenderer(this.#logger.getRenderer());
65
- lines.forEach((line) => {
66
- if (line.trim()) {
67
- sticker.add(line);
68
- }
69
- });
70
- sticker.render();
71
- return;
72
- }
73
- lines.forEach((line) => {
74
- if (line.trim()) {
75
- console.log(line);
76
- }
77
- });
78
- }
79
- #logAssetsDevServerMessage(data) {
80
- const dataString = data.toString();
81
- const lines = dataString.split('\n');
82
- lines.forEach((line) => {
83
- if (line.trim()) {
84
- console.log(line);
85
- }
86
- });
87
- }
88
- async #getPort() {
89
- if (process.env.PORT) {
90
- return getPort({ port: Number(process.env.PORT) });
91
- }
92
- const files = await new EnvLoader(this.#cwd).load();
93
- for (let file of files) {
94
- const envVariables = new EnvParser(file.contents).parse();
95
- if (envVariables.PORT) {
96
- return getPort({ port: Number(envVariables.PORT) });
97
- }
98
- }
99
- return getPort({ port: 3333 });
100
- }
75
+ /**
76
+ * Starts the HTTP server
77
+ */
101
78
  #startHTTPServer(port, mode) {
102
- this.#httpServerProcess = runNode(this.#cwd, {
79
+ this.#httpServer = runNode(this.#cwd, {
103
80
  script: this.#scriptFile,
104
81
  env: { PORT: port, ...this.#options.env },
105
82
  nodeArgs: this.#options.nodeArgs,
106
83
  scriptArgs: this.#options.scriptArgs,
107
84
  });
108
- this.#httpServerProcess.on('message', (message) => {
85
+ this.#httpServer.on('message', (message) => {
109
86
  if (this.#isAdonisJSReadyMessage(message)) {
110
87
  ui.sticker()
111
88
  .useColors(this.#colors)
@@ -115,172 +92,162 @@ export class DevServer {
115
92
  .render();
116
93
  }
117
94
  });
118
- this.#httpServerProcess
95
+ this.#httpServer
119
96
  .then((result) => {
120
- this.#logger.warning(`underlying HTTP server closed with status code "${result.exitCode}"`);
121
97
  if (mode === 'nonblocking') {
122
98
  this.#onClose?.(result.exitCode);
123
99
  this.#watcher?.close();
100
+ this.#assetsServer?.stop();
124
101
  }
125
102
  })
126
103
  .catch((error) => {
127
- this.#logger.warning('unable to connect to underlying HTTP server process');
128
- this.#logger.fatal(error);
129
- this.#onError?.(error);
130
- this.#watcher?.close();
104
+ if (mode === 'nonblocking') {
105
+ this.#onError?.(error);
106
+ this.#watcher?.close();
107
+ this.#assetsServer?.stop();
108
+ }
131
109
  });
132
110
  }
111
+ /**
112
+ * Starts the assets server
113
+ */
133
114
  #startAssetsServer() {
134
- const assetsBundler = this.#options.assets;
135
- if (!assetsBundler?.serve) {
136
- return;
115
+ this.#assetsServer = new AssetsDevServer(this.#cwd, this.#options.assets);
116
+ this.#assetsServer.setLogger(this.#logger);
117
+ this.#assetsServer.start();
118
+ }
119
+ /**
120
+ * Restarts the HTTP server
121
+ */
122
+ #restartHTTPServer(port) {
123
+ if (this.#httpServer) {
124
+ this.#httpServer.removeAllListeners();
125
+ this.#httpServer.kill('SIGKILL');
137
126
  }
138
- this.#logger.info(`starting "${assetsBundler.driver}" dev server...`);
139
- this.#assetsServerProcess = run(this.#cwd, {
140
- script: assetsBundler.cmd,
141
- stdio: 'pipe',
142
- scriptArgs: this.#options.scriptArgs,
143
- });
144
- this.#assetsServerProcess.stdout?.on('data', (data) => {
145
- if (assetsBundler.driver === 'vite') {
146
- this.#logViteDevServerMessage(data);
147
- }
148
- else {
149
- this.#logAssetsDevServerMessage(data);
150
- }
151
- });
152
- this.#assetsServerProcess.stderr?.on('data', (data) => {
153
- if (assetsBundler.driver === 'vite') {
154
- this.#logViteDevServerMessage(data);
155
- }
156
- else {
157
- this.#logAssetsDevServerMessage(data);
158
- }
159
- });
160
- this.#assetsServerProcess
161
- .then((result) => {
162
- this.#logger.warning(`"${assetsBundler.driver}" dev server closed with status code "${result.exitCode}"`);
163
- })
164
- .catch((error) => {
165
- this.#logger.warning(`unable to connect to "${assetsBundler.driver}" dev server`);
166
- this.#logger.fatal(error);
167
- });
127
+ this.#startHTTPServer(port, 'blocking');
168
128
  }
169
- #restart(port) {
170
- if (this.#httpServerProcess) {
171
- this.#httpServerProcess.removeAllListeners();
172
- this.#httpServerProcess.kill('SIGKILL');
129
+ /**
130
+ * Handles a non TypeScript file change
131
+ */
132
+ #handleFileChange(action, port, relativePath) {
133
+ if (isDotEnvFile(relativePath) || isRcFile(relativePath)) {
134
+ this.#clearScreen();
135
+ this.#logger.log(`${this.#colors.green(action)} ${relativePath}`);
136
+ this.#restartHTTPServer(port);
137
+ return;
173
138
  }
174
- this.#startHTTPServer(port, 'blocking');
139
+ if (this.#isMetaFileWithReloadsEnabled(relativePath)) {
140
+ this.#clearScreen();
141
+ this.#logger.log(`${this.#colors.green(action)} ${relativePath}`);
142
+ this.#restartHTTPServer(port);
143
+ return;
144
+ }
145
+ if (this.#isMetaFileWithReloadsDisabled(relativePath)) {
146
+ this.#clearScreen();
147
+ this.#logger.log(`${this.#colors.green(action)} ${relativePath}`);
148
+ }
149
+ }
150
+ /**
151
+ * Handles TypeScript source file change
152
+ */
153
+ #handleSourceFileChange(action, port, relativePath) {
154
+ this.#clearScreen();
155
+ this.#logger.log(`${this.#colors.green(action)} ${relativePath}`);
156
+ this.#restartHTTPServer(port);
175
157
  }
158
+ /**
159
+ * Set a custom CLI UI logger
160
+ */
176
161
  setLogger(logger) {
177
162
  this.#logger = logger;
163
+ this.#assetsServer?.setLogger(logger);
178
164
  return this;
179
165
  }
166
+ /**
167
+ * Add listener to get notified when dev server is
168
+ * closed
169
+ */
180
170
  onClose(callback) {
181
171
  this.#onClose = callback;
182
172
  return this;
183
173
  }
174
+ /**
175
+ * Add listener to get notified when dev server exists
176
+ * with an error
177
+ */
184
178
  onError(callback) {
185
179
  this.#onError = callback;
186
180
  return this;
187
181
  }
182
+ /**
183
+ * Close watchers and running child processes
184
+ */
185
+ async close() {
186
+ await this.#watcher?.close();
187
+ this.#assetsServer?.stop();
188
+ if (this.#httpServer) {
189
+ this.#httpServer.removeAllListeners();
190
+ this.#httpServer.kill('SIGKILL');
191
+ }
192
+ }
193
+ /**
194
+ * Start the development server
195
+ */
188
196
  async start() {
189
197
  this.#clearScreen();
190
198
  this.#logger.info('starting HTTP server...');
191
- this.#startHTTPServer(String(await this.#getPort()), 'nonblocking');
199
+ this.#startHTTPServer(String(await getPort(this.#cwd)), 'nonblocking');
192
200
  this.#startAssetsServer();
193
201
  }
202
+ /**
203
+ * Start the development server in watch mode
204
+ */
194
205
  async startAndWatch(ts, options) {
195
- const port = String(await this.#getPort());
206
+ const port = String(await getPort(this.#cwd));
196
207
  this.#isWatching = true;
197
208
  this.#clearScreen();
198
209
  this.#logger.info('starting HTTP server...');
199
210
  this.#startHTTPServer(port, 'blocking');
200
211
  this.#startAssetsServer();
212
+ /**
213
+ * Create watcher using tsconfig.json file
214
+ */
201
215
  const output = watch(this.#cwd, ts, options || {});
202
216
  if (!output) {
203
217
  this.#onClose?.(1);
204
218
  return;
205
219
  }
220
+ /**
221
+ * Storing reference to watcher, so that we can close it
222
+ * when HTTP server exists with error
223
+ */
206
224
  this.#watcher = output.chokidar;
225
+ /**
226
+ * Notify the watcher is ready
227
+ */
207
228
  output.watcher.on('watcher:ready', () => {
208
229
  this.#logger.info('watching file system for changes...');
209
230
  });
231
+ /**
232
+ * Cleanup when watcher dies
233
+ */
210
234
  output.chokidar.on('error', (error) => {
211
235
  this.#logger.warning('file system watcher failure');
212
236
  this.#logger.fatal(error);
213
237
  this.#onError?.(error);
214
238
  output.chokidar.close();
215
239
  });
216
- output.watcher.on('source:add', ({ relativePath }) => {
217
- this.#clearScreen();
218
- this.#logger.log(`${this.#colors.green('add')} ${relativePath}`);
219
- this.#restart(port);
220
- });
221
- output.watcher.on('source:change', ({ relativePath }) => {
222
- this.#clearScreen();
223
- this.#logger.log(`${this.#colors.green('update')} ${relativePath}`);
224
- this.#restart(port);
225
- });
226
- output.watcher.on('source:unlink', ({ relativePath }) => {
227
- this.#clearScreen();
228
- this.#logger.log(`${this.#colors.green('delete')} ${relativePath}`);
229
- this.#restart(port);
230
- });
231
- output.watcher.on('add', ({ relativePath }) => {
232
- if (this.#isDotEnvFile(relativePath) || this.#isRcFile(relativePath)) {
233
- this.#clearScreen();
234
- this.#logger.log(`${this.#colors.green('add')} ${relativePath}`);
235
- this.#restart(port);
236
- return;
237
- }
238
- if (this.#isMetaFileWithReloadsEnabled(relativePath)) {
239
- this.#clearScreen();
240
- this.#logger.log(`${this.#colors.green('add')} ${relativePath}`);
241
- this.#restart(port);
242
- return;
243
- }
244
- if (this.#isMetaFileWithReloadsDisabled(relativePath)) {
245
- this.#clearScreen();
246
- this.#logger.log(`${this.#colors.green('add')} ${relativePath}`);
247
- }
248
- });
249
- output.watcher.on('change', ({ relativePath }) => {
250
- if (this.#isDotEnvFile(relativePath) || this.#isRcFile(relativePath)) {
251
- this.#clearScreen();
252
- this.#logger.log(`${this.#colors.green('update')} ${relativePath}`);
253
- this.#restart(port);
254
- return;
255
- }
256
- if (this.#isMetaFileWithReloadsEnabled(relativePath)) {
257
- this.#clearScreen();
258
- this.#logger.log(`${this.#colors.green('update')} ${relativePath}`);
259
- this.#restart(port);
260
- return;
261
- }
262
- if (this.#isMetaFileWithReloadsDisabled(relativePath)) {
263
- this.#clearScreen();
264
- this.#logger.log(`${this.#colors.green('update')} ${relativePath}`);
265
- }
266
- });
267
- output.watcher.on('unlink', ({ relativePath }) => {
268
- if (this.#isDotEnvFile(relativePath) || this.#isRcFile(relativePath)) {
269
- this.#clearScreen();
270
- this.#logger.log(`${this.#colors.green('delete')} ${relativePath}`);
271
- this.#restart(port);
272
- return;
273
- }
274
- if (this.#isMetaFileWithReloadsEnabled(relativePath)) {
275
- this.#clearScreen();
276
- this.#logger.log(`${this.#colors.green('delete')} ${relativePath}`);
277
- this.#restart(port);
278
- return;
279
- }
280
- if (this.#isMetaFileWithReloadsDisabled(relativePath)) {
281
- this.#clearScreen();
282
- this.#logger.log(`${this.#colors.green('delete')} ${relativePath}`);
283
- }
284
- });
240
+ /**
241
+ * Changes in TypeScript source file
242
+ */
243
+ output.watcher.on('source:add', ({ relativePath }) => this.#handleSourceFileChange('add', port, relativePath));
244
+ output.watcher.on('source:change', ({ relativePath }) => this.#handleSourceFileChange('update', port, relativePath));
245
+ output.watcher.on('source:unlink', ({ relativePath }) => this.#handleSourceFileChange('delete', port, relativePath));
246
+ /**
247
+ * Changes in non-TypeScript source files
248
+ */
249
+ output.watcher.on('add', ({ relativePath }) => this.#handleFileChange('add', port, relativePath));
250
+ output.watcher.on('change', ({ relativePath }) => this.#handleFileChange('update', port, relativePath));
251
+ output.watcher.on('unlink', ({ relativePath }) => this.#handleFileChange('delete', port, relativePath));
285
252
  }
286
253
  }
@@ -0,0 +1,50 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import type tsStatic from 'typescript';
3
+ import { Watcher } from '@poppinss/chokidar-ts';
4
+ import type { RunOptions, WatchOptions } from './types.js';
5
+ /**
6
+ * Parses tsconfig.json and prints errors using typescript compiler
7
+ * host
8
+ */
9
+ export declare function parseConfig(cwd: string | URL, ts: typeof tsStatic): tsStatic.ParsedCommandLine | undefined;
10
+ /**
11
+ * Runs a Node.js script as a child process and inherits the stdio streams
12
+ */
13
+ export declare function runNode(cwd: string | URL, options: RunOptions): import("execa").ExecaChildProcess<string>;
14
+ /**
15
+ * Runs a script as a child process and inherits the stdio streams
16
+ */
17
+ export declare function run(cwd: string | URL, options: Omit<RunOptions, 'nodeArgs'>): import("execa").ExecaChildProcess<string>;
18
+ /**
19
+ * Watches the file system using tsconfig file
20
+ */
21
+ export declare function watch(cwd: string | URL, ts: typeof tsStatic, options: WatchOptions): {
22
+ watcher: Watcher;
23
+ chokidar: import("chokidar").FSWatcher;
24
+ } | undefined;
25
+ /**
26
+ * Check if file is an .env file
27
+ */
28
+ export declare function isDotEnvFile(filePath: string): boolean;
29
+ /**
30
+ * Check if file is .adonisrc.json file
31
+ */
32
+ export declare function isRcFile(filePath: string): boolean;
33
+ /**
34
+ * Returns the port to use after inspect the dot-env files inside
35
+ * a given directory.
36
+ *
37
+ * A random port is used when the specified port is in use. Following
38
+ * is the logic for finding a specified port.
39
+ *
40
+ * - The "process.env.PORT" value is used if exists.
41
+ * - The dot-env files are loaded using the "EnvLoader" and the PORT
42
+ * value is by iterating over all the loaded files. The iteration
43
+ * stops after first find.
44
+ */
45
+ export declare function getPort(cwd: URL): Promise<number>;
46
+ /**
47
+ * Helper function to copy files from relative paths or glob
48
+ * patterns
49
+ */
50
+ export declare function copyFiles(files: string[], cwd: string, outDir: string): Promise<string[]>;
@@ -0,0 +1,183 @@
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
+ }