diff --git a/src/bundler.ts b/src/bundler.ts index d426bdbf..0d9e3225 100644 --- a/src/bundler.ts +++ b/src/bundler.ts @@ -95,19 +95,6 @@ export class Bundler { } } - /** - * Copy files to destination directory - */ - async #copyFiles(files: string[], outDir: string) { - try { - await copyFiles(files, this.#cwdPath, outDir) - } catch (error) { - if (!error.message.includes("the file doesn't exist")) { - throw error - } - } - } - /** * Copy meta files to the output directory */ @@ -116,7 +103,7 @@ export class Bundler { .map((file) => file.pattern) .concat(additionalFilesToCopy) - await this.#copyFiles(metaFiles, outDir) + await copyFiles(metaFiles, this.#cwdPath, outDir) } /** @@ -189,7 +176,7 @@ export class Bundler { */ this.#logger.info('compiling typescript source', { suffix: 'tsc' }) const buildCompleted = await this.#runTsc(outDir) - await this.#copyFiles(['ace.js'], outDir) + await copyFiles(['ace.js'], this.#cwdPath, outDir) /** * Remove incomplete build directory when tsc build diff --git a/src/helpers.ts b/src/helpers.ts index 082d5b5b..58066cdc 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -7,14 +7,15 @@ * file that was distributed with this source code. */ -import cpy from 'cpy' import { isNotJunk } from 'junk' import fastGlob from 'fast-glob' import getRandomPort from 'get-port' +import { existsSync } from 'node:fs' import type tsStatic from 'typescript' import { fileURLToPath } from 'node:url' import { execaNode, execa } from 'execa' -import { isAbsolute, relative } from 'node:path' +import { copyFile, mkdir } from 'node:fs/promises' +import { dirname, isAbsolute, join, relative } from 'node:path' import { EnvLoader, EnvParser } from '@adonisjs/env' import { ConfigParser, Watcher } from '@poppinss/chokidar-ts' @@ -179,9 +180,18 @@ export async function copyFiles(files: string[], cwd: string, outDir: string) { */ const { paths, patterns } = files.reduce<{ patterns: string[]; paths: string[] }>( (result, file) => { + /** + * If file is a glob pattern, then push it to patterns + */ if (fastGlob.isDynamicPattern(file)) { result.patterns.push(file) - } else { + return result + } + + /** + * Otherwise, check if file exists and push it to paths to copy + */ + if (existsSync(join(cwd, file))) { result.paths.push(file) } @@ -198,14 +208,20 @@ export async function copyFiles(files: string[], cwd: string, outDir: string) { const filePaths = paths.concat(await fastGlob(patterns, { cwd })) /** - * Computing relative destination. This is because, cpy is buggy when - * outDir is an absolute path. + * Finally copy files to the destination by keeping the same + * directory structure and ignoring junk files */ - const destination = isAbsolute(outDir) ? relative(cwd, outDir) : outDir - debug('copying files %O to destination "%s"', filePaths, destination) + debug('copying files %O to destination "%s"', filePaths, outDir) + const copyPromises = filePaths.map(async (file) => { + const isJunkFile = !isNotJunk(file) + if (isJunkFile) return - return cpy(filePaths.filter(isNotJunk), destination, { - cwd: cwd, - flat: false, + const src = isAbsolute(file) ? file : join(cwd, file) + const dest = join(outDir, relative(cwd, src)) + + await mkdir(dirname(dest), { recursive: true }) + return copyFile(src, dest) }) + + return await Promise.all(copyPromises) } diff --git a/tests/bundler.spec.ts b/tests/bundler.spec.ts index 04c58b8d..c07670e8 100644 --- a/tests/bundler.spec.ts +++ b/tests/bundler.spec.ts @@ -41,4 +41,34 @@ test.group('Bundler', () => { assert.fileExists('./build/package-lock.json'), ]) }) + + test('should copy metafiles even if lock file is missing', async ({ assert, fs }) => { + await Promise.all([ + fs.create( + 'tsconfig.json', + JSON.stringify({ compilerOptions: { outDir: 'build', skipLibCheck: true } }) + ), + fs.create('adonisrc.ts', 'export default {}'), + fs.create('package.json', '{}'), + + fs.create('resources/views/app.edge', ''), + ]) + + const bundler = new Bundler(fs.baseUrl, ts, { + metaFiles: [ + { + pattern: 'resources/views/**/*.edge', + reloadServer: false, + }, + ], + }) + + await bundler.bundle(true, 'npm') + + await Promise.all([ + assert.fileExists('./build/resources/views/app.edge'), + assert.fileExists('./build/package.json'), + assert.fileExists('./build/adonisrc.js'), + ]) + }) }) diff --git a/tests/copy.spec.ts b/tests/copy.spec.ts new file mode 100644 index 00000000..7aed4afb --- /dev/null +++ b/tests/copy.spec.ts @@ -0,0 +1,57 @@ +import { test } from '@japa/runner' +import { copyFiles } from '../src/helpers.js' +import { join } from 'node:path' + +test.group('Copy files', () => { + test('match file patterns', async ({ fs }) => { + await fs.create('resources/views/welcome.edge', '') + await fs.create('resources/views/about.edge', '') + await fs.create('resources/views/contact/main.edge', '') + + await fs.create('public/foo/test/a.json', '') + await fs.create('public/foo/test/b/a.json', '') + + await copyFiles( + ['resources/views/*.edge', 'public/**'], + fs.basePath, + join(fs.basePath, 'build') + ) + + await fs.exists('build/resources/views/welcome.edge') + await fs.exists('build/resources/views/about.edge') + await fs.exists('build/resources/views/contact/main.edge') + + await fs.exists('build/public/foo/test/a.json') + await fs.exists('build/public/foo/test/b/a.json') + }) + + test('copy files that are not glob patterns', async ({ fs }) => { + await fs.create('resources/views/welcome.edge', '') + await fs.create('resources/views/about.edge', '') + await fs.create('package.json', '') + + await copyFiles( + ['resources/views/welcome.edge', 'resources/views/about.edge', 'package.json'], + fs.basePath, + join(fs.basePath, 'build') + ) + + await fs.exists('build/resources/views/welcome.edge') + await fs.exists('build/resources/views/about.edge') + await fs.exists('build/package.json') + }) + + test("copy files even if one path doesn't exist", async ({ fs }) => { + await fs.create('resources/views/welcome.edge', '') + await fs.create('resources/views/about.edge', '') + + await copyFiles( + ['resources/views/welcome.edge', 'resources/views/about.edge', 'package.json'], + fs.basePath, + join(fs.basePath, 'build') + ) + + await fs.exists('build/resources/views/welcome.edge') + await fs.exists('build/resources/views/about.edge') + }) +})