From c72034df31314257bc27a988e13ada88404a31f7 Mon Sep 17 00:00:00 2001 From: Harminder Virk Date: Fri, 3 Mar 2023 10:19:40 +0530 Subject: [PATCH] feat: build assets when starting dev server and building assets --- commands/build.ts | 81 +++++++++++++++++++++-------------------- commands/serve.ts | 76 +++++++++++++++++++------------------- modules/ace/shell.ts | 1 - package.json | 18 ++++----- src/internal_helpers.ts | 59 ++++++++++++++++++++++++++++++ 5 files changed, 147 insertions(+), 88 deletions(-) create mode 100644 src/internal_helpers.ts diff --git a/commands/build.ts b/commands/build.ts index 92d46671..338d5ccf 100644 --- a/commands/build.ts +++ b/commands/build.ts @@ -8,6 +8,7 @@ */ import { BaseCommand, flags } from '../modules/ace/main.js' +import { detectAssetsBundler, importAssembler, importTypeScript } from '../src/internal_helpers.js' /** * Serve command is used to run the AdonisJS HTTP server during development. The @@ -30,57 +31,59 @@ export default class Build extends BaseCommand { }) declare packageManager?: 'npm' | 'pnpm' | 'yarn' - /** - * Imports assembler and displays a human readable debugging message - */ - async #importAssembler(): Promise { - try { - return await this.app.import('@adonisjs/assembler') - } catch { - this.logger.error( - [ - 'Unable to import "@adonisjs/assembler"', - '', - 'The "@adonisjs/assembler" package is a development dependency and therefore you should use the build command during development only.', - '', - 'If you are using the build command inside a CI or with a deployment platform, make sure the NODE_ENV is set to "development"', - ].join('\n') - ) - this.exitCode = 1 - } - } + @flags.boolean({ + description: 'Build frontend assets', + showNegatedVariantInHelp: true, + default: true, + }) + declare assets?: boolean /** - * Imports typescript and displays a human readable debugging message + * Log a development dependency is missing */ - async #importTypeScript() { - try { - return await this.app.import('typescript') - } catch { - this.logger.error( - [ - 'Unable to import "typescript"', - '', - 'The "typescript" package is a development dependency and therefore you should use the build command during development only.', - '', - 'If you are using the build command inside a CI or with a deployment platform, make sure the NODE_ENV is set to "development"', - ].join('\n') - ) - this.exitCode = 1 - } + #logMissingDevelopmentDependency(dependency: string) { + this.logger.error( + [ + `Cannot find package "${dependency}"`, + '', + `The "${dependency}" package is a development dependency and therefore you should use the serve command during development only.`, + '', + 'If you are using the build command inside a CI or with a deployment platform, make sure the NODE_ENV is set to "development"', + ].join('\n') + ) } /** * Build application */ async run() { - const assembler = await this.#importAssembler() - const ts = await this.#importTypeScript() - if (!assembler || !ts) { + const assembler = await importAssembler(this.app) + if (!assembler) { + this.#logMissingDevelopmentDependency('@adonisjs/assembler') + this.exitCode = 1 + return + } + + const ts = await importTypeScript(this.app) + if (!ts) { + this.#logMissingDevelopmentDependency('typescript') + this.exitCode = 1 return } - const bundler = new assembler.Bundler(this.app.appRoot, ts.default, {}) + const assetsBundler = await detectAssetsBundler(this.app) + const bundler = new assembler.Bundler(this.app.appRoot, ts, { + assets: assetsBundler + ? { + serve: this.assets === false ? false : true, + driver: assetsBundler.name, + cmd: assetsBundler.devServerCommand, + } + : { + serve: false, + }, + metaFiles: this.app.rcFile.metaFiles, + }) /** * Share command logger with assembler, so that CLI flags like --no-ansi has diff --git a/commands/serve.ts b/commands/serve.ts index cc0f248b..c57b0799 100644 --- a/commands/serve.ts +++ b/commands/serve.ts @@ -7,8 +7,9 @@ * file that was distributed with this source code. */ +import type { CommandOptions } from '../types/ace.js' import { BaseCommand, flags } from '../modules/ace/main.js' -import { CommandOptions } from '../types/ace.js' +import { detectAssetsBundler, importAssembler, importTypeScript } from '../src/internal_helpers.js' /** * Serve command is used to run the AdonisJS HTTP server during development. The @@ -30,58 +31,52 @@ export default class Serve extends BaseCommand { @flags.boolean({ description: 'Use polling to detect filesystem changes' }) declare poll?: boolean - /** - * Imports assembler and displays a human readable debugging message - */ - async #importAssembler(): Promise { - try { - return await this.app.import('@adonisjs/assembler') - } catch { - this.logger.error( - [ - 'Unable to import "@adonisjs/assembler"', - '', - 'The "@adonisjs/assembler" package is a development dependency and therefore you should use the serve command during development only.', - '', - 'If you are running your application in production, then use "node bin/server.js" command to start the HTTP server', - ].join('\n') - ) - this.exitCode = 1 - } - } + @flags.boolean({ + description: 'Start assets bundler dev server', + showNegatedVariantInHelp: true, + default: true, + }) + declare assets?: boolean /** - * Imports typescript and displays a human readable debugging message + * Log a development dependency is missing */ - async #importTypeScript() { - try { - return await this.app.import('typescript') - } catch { - this.logger.error( - [ - 'Unable to import "typescript"', - '', - 'The "typescript" package is a development dependency and therefore you should use the serve command during development only.', - '', - 'If you are running your application in production, then use "node bin/server.js" command to start the HTTP server', - ].join('\n') - ) - this.exitCode = 1 - } + #logMissingDevelopmentDependency(dependency: string) { + this.logger.error( + [ + `Cannot find package "${dependency}"`, + '', + `The "${dependency}" package is a development dependency and therefore you should use the serve command during development only.`, + '', + 'If you are running your application in production, then use "node bin/server.js" command to start the HTTP server', + ].join('\n') + ) } /** * Runs the HTTP server */ async run() { - const assembler = await this.#importAssembler() + const assembler = await importAssembler(this.app) if (!assembler) { + this.#logMissingDevelopmentDependency('@adonisjs/assembler') + this.exitCode = 1 return } + const assetsBundler = await detectAssetsBundler(this.app) const devServer = new assembler.DevServer(this.app.appRoot, { nodeArgs: this.parsed.nodeArgs, scriptArgs: [], + assets: assetsBundler + ? { + serve: this.assets === false ? false : true, + driver: assetsBundler.name, + cmd: assetsBundler.devServerCommand, + } + : { + serve: false, + }, metaFiles: this.app.rcFile.metaFiles, }) @@ -109,11 +104,14 @@ export default class Serve extends BaseCommand { * Start the development server */ if (this.watch) { - const ts = await this.#importTypeScript() + const ts = await importTypeScript(this.app) if (!ts) { + this.#logMissingDevelopmentDependency('typescript') + this.exitCode = 1 return } - await devServer.startAndWatch(ts.default, { poll: this.poll || false }) + + await devServer.startAndWatch(ts, { poll: this.poll || false }) } else { await devServer.start() } diff --git a/modules/ace/shell.ts b/modules/ace/shell.ts index 48019d58..c37560f5 100644 --- a/modules/ace/shell.ts +++ b/modules/ace/shell.ts @@ -42,7 +42,6 @@ export function aceShell(cwd: URL) { const result = await childProcess process.exitCode = result.exitCode } catch (error) { - console.error(error) process.exitCode = 1 } }, diff --git a/package.json b/package.json index c4ca4536..0455608b 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "author": "virk,adonisjs", "license": "MIT", "devDependencies": { - "@adonisjs/assembler": "^6.1.0-0", + "@adonisjs/assembler": "^6.1.3-0", "@commitlint/cli": "^17.4.4", "@commitlint/config-conventional": "^17.4.4", "@japa/assert": "^1.4.1", @@ -111,7 +111,7 @@ "@japa/spec-reporter": "^1.3.3", "@swc/core": "^1.3.37", "@types/fs-extra": "^11.0.1", - "@types/node": "^18.14.2", + "@types/node": "^18.14.4", "@types/pretty-hrtime": "^1.0.1", "@types/sinon": "^10.0.13", "@types/supertest": "^2.0.12", @@ -124,7 +124,6 @@ "eslint-config-prettier": "^8.6.0", "eslint-plugin-adonis": "^3.0.3", "eslint-plugin-prettier": "^4.2.1", - "fs-extra": "^11.1.0", "get-port": "^6.1.2", "github-label-sync": "^2.2.0", "husky": "^8.0.3", @@ -139,30 +138,31 @@ }, "dependencies": { "@adonisjs/ace": "^12.3.0-0", - "@adonisjs/application": "^7.1.0-0", - "@adonisjs/bodyparser": "^9.3.0-0", + "@adonisjs/application": "^7.1.1-0", + "@adonisjs/bodyparser": "^9.3.1-0", "@adonisjs/config": "^4.2.0-0", "@adonisjs/encryption": "^5.1.2-0", "@adonisjs/env": "^4.2.0-0", - "@adonisjs/events": "^8.4.7-0", + "@adonisjs/events": "^8.4.8-0", "@adonisjs/fold": "^9.9.2-0", "@adonisjs/hash": "^8.3.1-0", - "@adonisjs/http-server": "^6.8.0-0", + "@adonisjs/http-server": "^6.8.1-0", "@adonisjs/logger": "^5.4.2-0", - "@adonisjs/validator": "^13.0.0-0", + "@adonisjs/validator": "^13.0.1-0", "@poppinss/macroable": "^1.0.0-2", "@poppinss/utils": "^6.5.0-0", "@sindresorhus/is": "^5.3.0", "@types/he": "^1.2.0", "cuid": "^3.0.0", "execa": "^7.0.0", + "fs-extra": "^11.1.0", "he": "^1.2.0", "pretty-hrtime": "^1.0.3", "youch": "^3.2.3", "youch-terminal": "^2.2.0" }, "peerDependencies": { - "@adonisjs/assembler": "^6.1.0-0", + "@adonisjs/assembler": "^6.1.3-0", "argon2": "^0.30.3", "bcrypt": "^5.0.1" }, diff --git a/src/internal_helpers.ts b/src/internal_helpers.ts new file mode 100644 index 00000000..d2287501 --- /dev/null +++ b/src/internal_helpers.ts @@ -0,0 +1,59 @@ +/* + * @adonisjs/core + * + * (c) AdonisJS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import fs from 'fs-extra' +import { ApplicationService } from './types.js' + +/** + * Imports assembler optionally + */ +export async function importAssembler( + app: ApplicationService +): Promise { + try { + return await app.import('@adonisjs/assembler') + } catch {} +} + +/** + * Imports typescript optionally + */ +export async function importTypeScript( + app: ApplicationService +): Promise { + try { + return await app.importDefault('typescript') + } catch {} +} + +/** + * Detects the assets bundler in use. The rcFile.assetsBundler is + * used when exists. + */ +export async function detectAssetsBundler(app: ApplicationService) { + if (app.rcFile.assetsBundler) { + return app.rcFile.assetsBundler + } + + if (await fs.exists(app.makePath('vite.config.js'))) { + return { + name: 'vite', + devServerCommand: 'vite', + buildCommand: 'vite build', + } + } + + if (await fs.exists(app.makePath('webpack.config.js'))) { + return { + name: 'encore', + devServerCommand: 'encore dev-server', + buildCommand: 'encore', + } + } +}