Skip to content

Commit

Permalink
feat(command): add vite integration
Browse files Browse the repository at this point in the history
  • Loading branch information
jlenon7 committed Dec 28, 2024
1 parent 72c2b30 commit dc012bf
Show file tree
Hide file tree
Showing 9 changed files with 1,208 additions and 24 deletions.
910 changes: 893 additions & 17 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@athenna/core",
"version": "5.2.0",
"version": "5.3.0",
"description": "One foundation for multiple applications.",
"license": "MIT",
"author": "João Lenon <[email protected]>",
Expand Down Expand Up @@ -105,7 +105,9 @@
"husky": "^3.1.0",
"lint-staged": "^12.5.0",
"nodemon": "^3.1.4",
"prettier": "^2.8.8"
"prettier": "^2.8.8",
"vite": "^6.0.6",
"vite-plugin-restart": "^0.4.2"
},
"c8": {
"all": true,
Expand Down
62 changes: 58 additions & 4 deletions src/commands/BuildCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,20 @@

import { rimraf } from 'rimraf'
import { tsc } from '@athenna/tsconfig/tsc'
import { Path, Color } from '@athenna/common'
import { BaseCommand } from '@athenna/artisan'
import { isAbsolute, join, parse } from 'node:path'
import { Path, Color, Module } from '@athenna/common'
import { BaseCommand, Option } from '@athenna/artisan'
import { copyfiles } from '@athenna/tsconfig/copyfiles'
import { UndefinedOutDirException } from '#src/exceptions/UndefinedOutDirException'

export class BuildCommand extends BaseCommand {
@Option({
signature: '-v, --vite',
description: 'Use vite to build your application static files.',
default: false
})
public vite: boolean

public static signature(): string {
return 'build'
}
Expand All @@ -40,7 +47,7 @@ export class BuildCommand extends BaseCommand {
}

const compiler = Color.yellow.bold('tsc')
const includedFiles = Color.gray(include.join(', '))
const includedPaths = Color.gray(include.join(', '))

const outDir = this.getOutDir()
const outDirName = Color.yellow.bold(parse(outDir).name)
Expand All @@ -56,11 +63,52 @@ export class BuildCommand extends BaseCommand {

if (include.length) {
tasks.addPromise(
`Copying included paths to ${outDirName} folder: ${includedFiles}`,
`Copying included paths to ${outDirName} folder: ${includedPaths}`,
() => copyfiles(include, outDir)
)
}

if (this.vite) {
const vite = this.getVite()

tasks.addPromise('Compiling static files using vite', async () => {
const defaultConfig = {
root: Path.pwd(),
assetsUrl: '/assets',
buildDirectory: 'public/assets',
logLevel: Config.get('rc.bootLogs', true) ? 'info' : 'silent',
build: {
assetsDir: '',
manifest: true,
emptyOutDir: true,
outDir: 'public/assets',
assetsInlineLimit: 0,
rollupOptions: {
output: {
entryFileNames: '[name].js',
chunkFileNames: '[name].js',
assetFileNames: '[name].[ext]'
},
input: ['src/resources/css/app.scss', 'src/resources/js/app.js']
}
}
}

const { config: fileConfig } = await vite.loadConfigFromFile(
{
command: 'build',
mode: 'production'
},
undefined,
Path.pwd()
)

const config = vite.mergeConfig(defaultConfig, fileConfig)

return vite.build(config)
})
}

await tasks.run()

console.log()
Expand All @@ -84,4 +132,10 @@ export class BuildCommand extends BaseCommand {

return Path.pwd(Config.get('rc.commands.build.outDir'))
}

public getVite() {
const require = Module.createRequire(import.meta.url)

return require('vite')
}
}
70 changes: 69 additions & 1 deletion src/commands/ServeCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,19 @@ import { BaseCommand, Option } from '@athenna/artisan'
export class ServeCommand extends BaseCommand {
@Option({
signature: '-w, --watch',
description: 'Use nodemon to watch the application and restart on changes.',
description:
'Use nodemon to watch the application and restart on changes (also turn on vite --watch if using it).',
default: false
})
public watch: boolean

@Option({
signature: '-v, --vite',
description: 'Use vite to build your application static files.',
default: false
})
public vite: boolean

public static signature(): string {
return 'serve'
}
Expand All @@ -34,6 +42,54 @@ export class ServeCommand extends BaseCommand {
Path.bin(`main.${Path.ext()}`)
)

if (this.vite) {
const vite = this.getVite()
const PluginRestart = this.getVitePluginRestart()

const defaultConfig = {
root: Path.pwd(),
assetsUrl: '/assets',
buildDirectory: 'public/assets',
logLevel: Config.get('rc.bootLogs', true) ? 'info' : 'silent',
build: {
watch: this.watch
? {
clearScreen: true,
include: 'src/resources/views/**/*.edge',
exclude: 'node_modules/**'
}
: undefined,
assetsDir: '',
manifest: true,
emptyOutDir: true,
outDir: 'public/assets',
assetsInlineLimit: 0,
rollupOptions: {
output: {
entryFileNames: '[name].js',
chunkFileNames: '[name].js',
assetFileNames: '[name].[ext]'
},
input: ['src/resources/css/app.scss', 'src/resources/js/app.js']
}
},
plugins: [PluginRestart({ reload: ['src/resources/views/**/*.edge'] })]
}

const { config: fileConfig } = await vite.loadConfigFromFile(
{
command: 'build',
mode: 'development'
},
undefined,
Path.pwd()
)

const config = vite.mergeConfig(defaultConfig, fileConfig)

vite.build(config)
}

if (this.watch) {
const nodemon = this.getNodemon()

Expand Down Expand Up @@ -85,6 +141,18 @@ export class ServeCommand extends BaseCommand {
await Module.resolve(entrypoint, Config.get('rc.parentURL'))
}

public getVite() {
const require = Module.createRequire(import.meta.url)

return require('vite')
}

public getVitePluginRestart() {
const require = Module.createRequire(import.meta.url)

return require('vite-plugin-restart')
}

public getNodemon() {
const require = Module.createRequire(import.meta.url)

Expand Down
48 changes: 48 additions & 0 deletions tests/fixtures/consoles/build-vite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* @athenna/core
*
* (c) João Lenon <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

import { Mock } from '@athenna/test'
import { Path } from '@athenna/common'
import { ViewProvider } from '@athenna/view'
import { Rc, Config } from '@athenna/config'
import { LoggerProvider } from '@athenna/logger'
import { BuildCommand } from '#src/commands/BuildCommand'
import { Artisan, ConsoleKernel, ArtisanProvider } from '@athenna/artisan'

new ViewProvider().register()
new LoggerProvider().register()
new ArtisanProvider().register()

await Config.loadAll(Path.fixtures('config'))

await Rc.setFile(Path.pwd('package.json'))

Path.mergeDirs(Config.get('rc.directories', {}))

await new ConsoleKernel().registerCommands()

const vite = function () {
return Mock.fake()
}

vite.loadConfigFromFile = function () {
return { config: {} }
}

vite.mergeConfig = function () {
return {}
}

vite.build = function () {
return this
}

Mock.when(BuildCommand.prototype, 'getVite').return(vite)

await Artisan.parse(process.argv)
55 changes: 55 additions & 0 deletions tests/fixtures/consoles/serve-vite-with-logs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* @athenna/core
*
* (c) João Lenon <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

import { Mock } from '@athenna/test'
import { Path } from '@athenna/common'
import { ViewProvider } from '@athenna/view'
import { Rc, Config } from '@athenna/config'
import { LoggerProvider } from '@athenna/logger'
import { ServeCommand } from '#src/commands/ServeCommand'
import { Artisan, ConsoleKernel, ArtisanProvider } from '@athenna/artisan'

new ViewProvider().register()
new LoggerProvider().register()
new ArtisanProvider().register()

await Config.loadAll(Path.fixtures('config'))

await Rc.setFile(Path.pwd('package.json'))

Path.mergeDirs(Config.get('rc.directories', {}))

await new ConsoleKernel().registerCommands()

const vite = function () {
return Mock.fake()
}

vite.loadConfigFromFile = function () {
return { config: {} }
}

vite.mergeConfig = function () {
return {}
}

vite.build = function () {
return this
}

const vitePluginRestart = function () {
return Mock.fake()
}

Config.set('rc.bootLogs', true)

Mock.when(ServeCommand.prototype, 'getVite').return(vite)
Mock.when(ServeCommand.prototype, 'getVitePluginRestart').return(vitePluginRestart)

await Artisan.parse(process.argv)
53 changes: 53 additions & 0 deletions tests/fixtures/consoles/serve-vite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* @athenna/core
*
* (c) João Lenon <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

import { Mock } from '@athenna/test'
import { Path } from '@athenna/common'
import { ViewProvider } from '@athenna/view'
import { Rc, Config } from '@athenna/config'
import { LoggerProvider } from '@athenna/logger'
import { ServeCommand } from '#src/commands/ServeCommand'
import { Artisan, ConsoleKernel, ArtisanProvider } from '@athenna/artisan'

new ViewProvider().register()
new LoggerProvider().register()
new ArtisanProvider().register()

await Config.loadAll(Path.fixtures('config'))

await Rc.setFile(Path.pwd('package.json'))

Path.mergeDirs(Config.get('rc.directories', {}))

await new ConsoleKernel().registerCommands()

const vite = function () {
return Mock.fake()
}

vite.loadConfigFromFile = function () {
return { config: {} }
}

vite.mergeConfig = function () {
return {}
}

vite.build = function () {
return this
}

const vitePluginRestart = function () {
return Mock.fake()
}

Mock.when(ServeCommand.prototype, 'getVite').return(vite)
Mock.when(ServeCommand.prototype, 'getVitePluginRestart').return(vitePluginRestart)

await Artisan.parse(process.argv)
10 changes: 10 additions & 0 deletions tests/unit/commands/BuildCommandTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ export default class BuildCommandTest extends BaseCommandTest {
assert.isTrue(File.existsSync(Path.pwd('build-relative/app/hello.d.ts')))
}

@Test()
public async shouldBeAbleToRunBuildCommandWithVite({ command }: Context) {
const output = await command.run('build --vite', {
path: Path.fixtures('consoles/build-vite.ts')
})

output.assertSucceeded()
output.assertLogged('Application successfully compiled')
}

@Test()
public async shouldDeleteTheOldOutDirBeforeCompilingTheApplicationAgain({ assert, command }: Context) {
await command.run('build')
Expand Down
Loading

0 comments on commit dc012bf

Please sign in to comment.