Skip to content

Commit 98ae3a1

Browse files
authored
Merge pull request #237 from AthennaIO/develop
feat(command): add vite integration
2 parents d773548 + dc012bf commit 98ae3a1

9 files changed

Lines changed: 1208 additions & 24 deletions

File tree

package-lock.json

Lines changed: 893 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@athenna/core",
3-
"version": "5.2.0",
3+
"version": "5.3.0",
44
"description": "One foundation for multiple applications.",
55
"license": "MIT",
66
"author": "João Lenon <lenon@athenna.io>",
@@ -105,7 +105,9 @@
105105
"husky": "^3.1.0",
106106
"lint-staged": "^12.5.0",
107107
"nodemon": "^3.1.4",
108-
"prettier": "^2.8.8"
108+
"prettier": "^2.8.8",
109+
"vite": "^6.0.6",
110+
"vite-plugin-restart": "^0.4.2"
109111
},
110112
"c8": {
111113
"all": true,

src/commands/BuildCommand.ts

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,20 @@
99

1010
import { rimraf } from 'rimraf'
1111
import { tsc } from '@athenna/tsconfig/tsc'
12-
import { Path, Color } from '@athenna/common'
13-
import { BaseCommand } from '@athenna/artisan'
1412
import { isAbsolute, join, parse } from 'node:path'
13+
import { Path, Color, Module } from '@athenna/common'
14+
import { BaseCommand, Option } from '@athenna/artisan'
1515
import { copyfiles } from '@athenna/tsconfig/copyfiles'
1616
import { UndefinedOutDirException } from '#src/exceptions/UndefinedOutDirException'
1717

1818
export class BuildCommand extends BaseCommand {
19+
@Option({
20+
signature: '-v, --vite',
21+
description: 'Use vite to build your application static files.',
22+
default: false
23+
})
24+
public vite: boolean
25+
1926
public static signature(): string {
2027
return 'build'
2128
}
@@ -40,7 +47,7 @@ export class BuildCommand extends BaseCommand {
4047
}
4148

4249
const compiler = Color.yellow.bold('tsc')
43-
const includedFiles = Color.gray(include.join(', '))
50+
const includedPaths = Color.gray(include.join(', '))
4451

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

5764
if (include.length) {
5865
tasks.addPromise(
59-
`Copying included paths to ${outDirName} folder: ${includedFiles}`,
66+
`Copying included paths to ${outDirName} folder: ${includedPaths}`,
6067
() => copyfiles(include, outDir)
6168
)
6269
}
6370

71+
if (this.vite) {
72+
const vite = this.getVite()
73+
74+
tasks.addPromise('Compiling static files using vite', async () => {
75+
const defaultConfig = {
76+
root: Path.pwd(),
77+
assetsUrl: '/assets',
78+
buildDirectory: 'public/assets',
79+
logLevel: Config.get('rc.bootLogs', true) ? 'info' : 'silent',
80+
build: {
81+
assetsDir: '',
82+
manifest: true,
83+
emptyOutDir: true,
84+
outDir: 'public/assets',
85+
assetsInlineLimit: 0,
86+
rollupOptions: {
87+
output: {
88+
entryFileNames: '[name].js',
89+
chunkFileNames: '[name].js',
90+
assetFileNames: '[name].[ext]'
91+
},
92+
input: ['src/resources/css/app.scss', 'src/resources/js/app.js']
93+
}
94+
}
95+
}
96+
97+
const { config: fileConfig } = await vite.loadConfigFromFile(
98+
{
99+
command: 'build',
100+
mode: 'production'
101+
},
102+
undefined,
103+
Path.pwd()
104+
)
105+
106+
const config = vite.mergeConfig(defaultConfig, fileConfig)
107+
108+
return vite.build(config)
109+
})
110+
}
111+
64112
await tasks.run()
65113

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

85133
return Path.pwd(Config.get('rc.commands.build.outDir'))
86134
}
135+
136+
public getVite() {
137+
const require = Module.createRequire(import.meta.url)
138+
139+
return require('vite')
140+
}
87141
}

src/commands/ServeCommand.ts

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,19 @@ import { BaseCommand, Option } from '@athenna/artisan'
1515
export class ServeCommand extends BaseCommand {
1616
@Option({
1717
signature: '-w, --watch',
18-
description: 'Use nodemon to watch the application and restart on changes.',
18+
description:
19+
'Use nodemon to watch the application and restart on changes (also turn on vite --watch if using it).',
1920
default: false
2021
})
2122
public watch: boolean
2223

24+
@Option({
25+
signature: '-v, --vite',
26+
description: 'Use vite to build your application static files.',
27+
default: false
28+
})
29+
public vite: boolean
30+
2331
public static signature(): string {
2432
return 'serve'
2533
}
@@ -34,6 +42,54 @@ export class ServeCommand extends BaseCommand {
3442
Path.bin(`main.${Path.ext()}`)
3543
)
3644

45+
if (this.vite) {
46+
const vite = this.getVite()
47+
const PluginRestart = this.getVitePluginRestart()
48+
49+
const defaultConfig = {
50+
root: Path.pwd(),
51+
assetsUrl: '/assets',
52+
buildDirectory: 'public/assets',
53+
logLevel: Config.get('rc.bootLogs', true) ? 'info' : 'silent',
54+
build: {
55+
watch: this.watch
56+
? {
57+
clearScreen: true,
58+
include: 'src/resources/views/**/*.edge',
59+
exclude: 'node_modules/**'
60+
}
61+
: undefined,
62+
assetsDir: '',
63+
manifest: true,
64+
emptyOutDir: true,
65+
outDir: 'public/assets',
66+
assetsInlineLimit: 0,
67+
rollupOptions: {
68+
output: {
69+
entryFileNames: '[name].js',
70+
chunkFileNames: '[name].js',
71+
assetFileNames: '[name].[ext]'
72+
},
73+
input: ['src/resources/css/app.scss', 'src/resources/js/app.js']
74+
}
75+
},
76+
plugins: [PluginRestart({ reload: ['src/resources/views/**/*.edge'] })]
77+
}
78+
79+
const { config: fileConfig } = await vite.loadConfigFromFile(
80+
{
81+
command: 'build',
82+
mode: 'development'
83+
},
84+
undefined,
85+
Path.pwd()
86+
)
87+
88+
const config = vite.mergeConfig(defaultConfig, fileConfig)
89+
90+
vite.build(config)
91+
}
92+
3793
if (this.watch) {
3894
const nodemon = this.getNodemon()
3995

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

144+
public getVite() {
145+
const require = Module.createRequire(import.meta.url)
146+
147+
return require('vite')
148+
}
149+
150+
public getVitePluginRestart() {
151+
const require = Module.createRequire(import.meta.url)
152+
153+
return require('vite-plugin-restart')
154+
}
155+
88156
public getNodemon() {
89157
const require = Module.createRequire(import.meta.url)
90158

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* @athenna/core
3+
*
4+
* (c) João Lenon <lenon@athenna.io>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
import { Mock } from '@athenna/test'
11+
import { Path } from '@athenna/common'
12+
import { ViewProvider } from '@athenna/view'
13+
import { Rc, Config } from '@athenna/config'
14+
import { LoggerProvider } from '@athenna/logger'
15+
import { BuildCommand } from '#src/commands/BuildCommand'
16+
import { Artisan, ConsoleKernel, ArtisanProvider } from '@athenna/artisan'
17+
18+
new ViewProvider().register()
19+
new LoggerProvider().register()
20+
new ArtisanProvider().register()
21+
22+
await Config.loadAll(Path.fixtures('config'))
23+
24+
await Rc.setFile(Path.pwd('package.json'))
25+
26+
Path.mergeDirs(Config.get('rc.directories', {}))
27+
28+
await new ConsoleKernel().registerCommands()
29+
30+
const vite = function () {
31+
return Mock.fake()
32+
}
33+
34+
vite.loadConfigFromFile = function () {
35+
return { config: {} }
36+
}
37+
38+
vite.mergeConfig = function () {
39+
return {}
40+
}
41+
42+
vite.build = function () {
43+
return this
44+
}
45+
46+
Mock.when(BuildCommand.prototype, 'getVite').return(vite)
47+
48+
await Artisan.parse(process.argv)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* @athenna/core
3+
*
4+
* (c) João Lenon <lenon@athenna.io>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
import { Mock } from '@athenna/test'
11+
import { Path } from '@athenna/common'
12+
import { ViewProvider } from '@athenna/view'
13+
import { Rc, Config } from '@athenna/config'
14+
import { LoggerProvider } from '@athenna/logger'
15+
import { ServeCommand } from '#src/commands/ServeCommand'
16+
import { Artisan, ConsoleKernel, ArtisanProvider } from '@athenna/artisan'
17+
18+
new ViewProvider().register()
19+
new LoggerProvider().register()
20+
new ArtisanProvider().register()
21+
22+
await Config.loadAll(Path.fixtures('config'))
23+
24+
await Rc.setFile(Path.pwd('package.json'))
25+
26+
Path.mergeDirs(Config.get('rc.directories', {}))
27+
28+
await new ConsoleKernel().registerCommands()
29+
30+
const vite = function () {
31+
return Mock.fake()
32+
}
33+
34+
vite.loadConfigFromFile = function () {
35+
return { config: {} }
36+
}
37+
38+
vite.mergeConfig = function () {
39+
return {}
40+
}
41+
42+
vite.build = function () {
43+
return this
44+
}
45+
46+
const vitePluginRestart = function () {
47+
return Mock.fake()
48+
}
49+
50+
Config.set('rc.bootLogs', true)
51+
52+
Mock.when(ServeCommand.prototype, 'getVite').return(vite)
53+
Mock.when(ServeCommand.prototype, 'getVitePluginRestart').return(vitePluginRestart)
54+
55+
await Artisan.parse(process.argv)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* @athenna/core
3+
*
4+
* (c) João Lenon <lenon@athenna.io>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
import { Mock } from '@athenna/test'
11+
import { Path } from '@athenna/common'
12+
import { ViewProvider } from '@athenna/view'
13+
import { Rc, Config } from '@athenna/config'
14+
import { LoggerProvider } from '@athenna/logger'
15+
import { ServeCommand } from '#src/commands/ServeCommand'
16+
import { Artisan, ConsoleKernel, ArtisanProvider } from '@athenna/artisan'
17+
18+
new ViewProvider().register()
19+
new LoggerProvider().register()
20+
new ArtisanProvider().register()
21+
22+
await Config.loadAll(Path.fixtures('config'))
23+
24+
await Rc.setFile(Path.pwd('package.json'))
25+
26+
Path.mergeDirs(Config.get('rc.directories', {}))
27+
28+
await new ConsoleKernel().registerCommands()
29+
30+
const vite = function () {
31+
return Mock.fake()
32+
}
33+
34+
vite.loadConfigFromFile = function () {
35+
return { config: {} }
36+
}
37+
38+
vite.mergeConfig = function () {
39+
return {}
40+
}
41+
42+
vite.build = function () {
43+
return this
44+
}
45+
46+
const vitePluginRestart = function () {
47+
return Mock.fake()
48+
}
49+
50+
Mock.when(ServeCommand.prototype, 'getVite').return(vite)
51+
Mock.when(ServeCommand.prototype, 'getVitePluginRestart').return(vitePluginRestart)
52+
53+
await Artisan.parse(process.argv)

tests/unit/commands/BuildCommandTest.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@ export default class BuildCommandTest extends BaseCommandTest {
4040
assert.isTrue(File.existsSync(Path.pwd('build-relative/app/hello.d.ts')))
4141
}
4242

43+
@Test()
44+
public async shouldBeAbleToRunBuildCommandWithVite({ command }: Context) {
45+
const output = await command.run('build --vite', {
46+
path: Path.fixtures('consoles/build-vite.ts')
47+
})
48+
49+
output.assertSucceeded()
50+
output.assertLogged('Application successfully compiled')
51+
}
52+
4353
@Test()
4454
public async shouldDeleteTheOldOutDirBeforeCompilingTheApplicationAgain({ assert, command }: Context) {
4555
await command.run('build')

0 commit comments

Comments
 (0)