Skip to content

Commit d7b8944

Browse files
committed
feat: build assets when running build command
1 parent af69a6f commit d7b8944

File tree

6 files changed

+204
-72
lines changed

6 files changed

+204
-72
lines changed

src/bundler.ts

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@
1010
import fs from 'fs-extra'
1111
import slash from 'slash'
1212
import copyfiles from 'cpy'
13-
import { execa } from 'execa'
14-
import { join, relative } from 'node:path'
1513
import type tsStatic from 'typescript'
1614
import { fileURLToPath } from 'node:url'
17-
import { ConfigParser } from '@poppinss/chokidar-ts'
15+
import { join, relative } from 'node:path'
1816
import { cliui, type Logger } from '@poppinss/cliui'
1917

18+
import { run } from './run.js'
19+
import { parseConfig } from './parse_config.js'
2020
import type { BundlerOptions } from './types.js'
2121

2222
/**
@@ -59,18 +59,37 @@ export class Bundler {
5959
await fs.remove(outDir)
6060
}
6161

62+
/**
63+
* Runs assets bundler command to build assets.
64+
*/
65+
async #buildAssets(): Promise<boolean> {
66+
const assetsBundler = this.#options.assets
67+
if (!assetsBundler?.serve) {
68+
return true
69+
}
70+
71+
try {
72+
this.#logger.info('compiling frontend assets', { suffix: assetsBundler.cmd })
73+
await run(this.#cwd, {
74+
stdio: 'inherit',
75+
script: assetsBundler.cmd,
76+
scriptArgs: [],
77+
})
78+
return true
79+
} catch {
80+
return false
81+
}
82+
}
83+
6284
/**
6385
* Runs tsc command to build the source.
6486
*/
65-
async #runTsc(outDir: string) {
87+
async #runTsc(outDir: string): Promise<boolean> {
6688
try {
67-
await execa('tsc', ['--outDir', outDir], {
68-
cwd: this.#cwd,
69-
preferLocal: true,
70-
localDir: this.#cwd,
71-
windowsHide: false,
72-
buffer: false,
89+
await run(this.#cwd, {
7390
stdio: 'inherit',
91+
script: 'tsc',
92+
scriptArgs: ['--outDir', outDir],
7493
})
7594
return true
7695
} catch {
@@ -161,21 +180,7 @@ export class Bundler {
161180
/**
162181
* Step 1: Parse config file to get the build output directory
163182
*/
164-
const { config, error } = new ConfigParser(this.#cwd, 'tsconfig.json', this.#ts).parse()
165-
if (error) {
166-
const compilerHost = this.#ts.createCompilerHost({})
167-
this.#logger.logError(this.#ts.formatDiagnosticsWithColorAndContext([error], compilerHost))
168-
return false
169-
}
170-
171-
if (config!.errors.length) {
172-
const compilerHost = this.#ts.createCompilerHost({})
173-
this.#logger.logError(
174-
this.#ts.formatDiagnosticsWithColorAndContext(config!.errors, compilerHost)
175-
)
176-
return false
177-
}
178-
183+
const config = parseConfig(this.#cwd, this.#ts)
179184
if (!config) {
180185
return false
181186
}
@@ -188,7 +193,14 @@ export class Bundler {
188193
await this.#cleanupBuildDirectory(outDir)
189194

190195
/**
191-
* Step 3: Build typescript source code
196+
* Step 3: Build frontend assets
197+
*/
198+
if (!(await this.#buildAssets())) {
199+
return false
200+
}
201+
202+
/**
203+
* Step 4: Build typescript source code
192204
*/
193205
this.#logger.info('compiling typescript source', { suffix: 'tsc' })
194206
const buildCompleted = await this.#runTsc(outDir)
@@ -219,21 +231,24 @@ export class Bundler {
219231
}
220232

221233
/**
222-
* Step 4: Copy meta files to the build directory
234+
* Step 5: Copy meta files to the build directory
223235
*/
224236
const pkgFiles = ['package.json', this.#getClientLockFile(client)]
225237
this.#logger.info('copying meta files to the output directory')
226238
await this.#copyMetaFiles(outDir, pkgFiles)
227239

228240
/**
229-
* Step 5: Copy .adonisrc.json file to the build directory
241+
* Step 6: Copy .adonisrc.json file to the build directory
230242
*/
231243
this.#logger.info('copying .adonisrc.json file to the output directory')
232244
await this.#copyAdonisRcFile(outDir)
233245

234246
this.#logger.success('build completed')
235247
this.#logger.log('')
236248

249+
/**
250+
* Next steps
251+
*/
237252
ui.instructions()
238253
.useRenderer(this.#logger.getRenderer())
239254
.heading('Run the following commands to start the server in production')

src/dev_server.ts

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,62 @@ export class DevServer {
115115
}
116116
}
117117

118+
/**
119+
* Logs messages from vite dev server stdout and stderr
120+
*/
121+
#logViteDevServerMessage(data: Buffer) {
122+
const dataString = data.toString()
123+
const lines = dataString.split('\n')
124+
125+
/**
126+
* Logging VITE ready in message with proper
127+
* spaces and newlines
128+
*/
129+
if (dataString.includes('ready in')) {
130+
console.log('')
131+
console.log(dataString.trim())
132+
return
133+
}
134+
135+
/**
136+
* Put a wrapper around vite network address log
137+
*/
138+
if (dataString.includes('Local') && dataString.includes('Network')) {
139+
const sticker = ui.sticker().useColors(this.#colors).useRenderer(this.#logger.getRenderer())
140+
141+
lines.forEach((line: string) => {
142+
if (line.trim()) {
143+
sticker.add(line)
144+
}
145+
})
146+
147+
sticker.render()
148+
return
149+
}
150+
151+
/**
152+
* Log rest of the lines
153+
*/
154+
lines.forEach((line: string) => {
155+
if (line.trim()) {
156+
console.log(line)
157+
}
158+
})
159+
}
160+
161+
/**
162+
* Logs messages from assets dev server stdout and stderr
163+
*/
164+
#logAssetsDevServerMessage(data: Buffer) {
165+
const dataString = data.toString()
166+
const lines = dataString.split('\n')
167+
lines.forEach((line: string) => {
168+
if (line.trim()) {
169+
console.log(line)
170+
}
171+
})
172+
}
173+
118174
/**
119175
* Returns PORT for starting the HTTP server with option to use
120176
* a random PORT if main PORT is in use.
@@ -192,11 +248,36 @@ export class DevServer {
192248
}
193249

194250
this.#logger.info(`starting "${assetsBundler.driver}" dev server...`)
195-
this.#assetsServerProcess = run(assetsBundler.cmd, {
196-
script: this.#scriptFile,
251+
this.#assetsServerProcess = run(this.#cwd, {
252+
script: assetsBundler.cmd,
253+
254+
/**
255+
* We do not inherit the stdio for vite and encore, because they then
256+
* own the stdin and interrupts the `Ctrl + C`.
257+
*/
258+
stdio: 'pipe',
197259
scriptArgs: this.#options.scriptArgs,
198260
})
199261

262+
/**
263+
* Log child process messages
264+
*/
265+
this.#assetsServerProcess.stdout?.on('data', (data) => {
266+
if (assetsBundler.driver === 'vite') {
267+
this.#logViteDevServerMessage(data)
268+
} else {
269+
this.#logAssetsDevServerMessage(data)
270+
}
271+
})
272+
273+
this.#assetsServerProcess.stderr?.on('data', (data) => {
274+
if (assetsBundler.driver === 'vite') {
275+
this.#logViteDevServerMessage(data)
276+
} else {
277+
this.#logAssetsDevServerMessage(data)
278+
}
279+
})
280+
200281
this.#assetsServerProcess
201282
.then((result) => {
202283
this.#logger.warning(

src/parse_config.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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+
10+
import type tsStatic from 'typescript'
11+
import { ConfigParser } from '@poppinss/chokidar-ts'
12+
13+
/**
14+
* Parses tsconfig.json and prints errors using typescript compiler
15+
* host
16+
*/
17+
export function parseConfig(cwd: string | URL, ts: typeof tsStatic) {
18+
const { config, error } = new ConfigParser(cwd, 'tsconfig.json', ts).parse()
19+
if (error) {
20+
const compilerHost = ts.createCompilerHost({})
21+
console.log(ts.formatDiagnosticsWithColorAndContext([error], compilerHost))
22+
return
23+
}
24+
25+
if (config!.errors.length) {
26+
const compilerHost = ts.createCompilerHost({})
27+
console.log(ts.formatDiagnosticsWithColorAndContext(config!.errors, compilerHost))
28+
return
29+
}
30+
31+
return config
32+
}

src/run.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,33 +27,39 @@ const DEFAULT_NODE_ARGS = [
2727
* Runs a Node.js script as a child process and inherits the stdio streams
2828
*/
2929
export function runNode(cwd: string | URL, options: RunOptions) {
30-
const childProces = execaNode(options.script, options.scriptArgs, {
30+
const childProcess = execaNode(options.script, options.scriptArgs, {
3131
nodeOptions: DEFAULT_NODE_ARGS.concat(options.nodeArgs),
3232
preferLocal: true,
3333
windowsHide: false,
3434
localDir: cwd,
3535
cwd,
3636
buffer: false,
37-
stdio: 'inherit',
38-
env: options.env,
37+
stdio: options.stdio || 'inherit',
38+
env: {
39+
...(options.stdio === 'pipe' ? { FORCE_COLOR: 'true' } : {}),
40+
...options.env,
41+
},
3942
})
4043

41-
return childProces
44+
return childProcess
4245
}
4346

4447
/**
4548
* Runs a script as a child process and inherits the stdio streams
4649
*/
4750
export function run(cwd: string | URL, options: Omit<RunOptions, 'nodeArgs'>) {
48-
const childProces = execa(options.script, options.scriptArgs, {
51+
const childProcess = execa(options.script, options.scriptArgs, {
4952
preferLocal: true,
5053
windowsHide: false,
5154
localDir: cwd,
5255
cwd,
5356
buffer: false,
54-
stdio: 'inherit',
55-
env: options.env,
57+
stdio: options.stdio || 'inherit',
58+
env: {
59+
...(options.stdio === 'pipe' ? { FORCE_COLOR: 'true' } : {}),
60+
...options.env,
61+
},
5662
})
5763

58-
return childProces
64+
return childProcess
5965
}

src/types.ts

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export type RunOptions = {
1414
script: string
1515
scriptArgs: string[]
1616
nodeArgs: string[]
17+
stdio?: 'pipe' | 'inherit'
1718
env?: NodeJS.ProcessEnv
1819
}
1920

@@ -24,6 +25,29 @@ export type WatchOptions = {
2425
poll?: boolean
2526
}
2627

28+
/**
29+
* Meta file config defined in ".adonisrc.json" file
30+
*/
31+
export type MetaFile = {
32+
pattern: string
33+
reloadServer: boolean
34+
}
35+
36+
/**
37+
* Options accepted by assets bundler
38+
*/
39+
export type AssetsBundlerOptions =
40+
| {
41+
serve: false
42+
driver?: string
43+
cmd?: string
44+
}
45+
| {
46+
serve: true
47+
driver: string
48+
cmd: string
49+
}
50+
2751
/**
2852
* Options accepted by the dev server
2953
*/
@@ -32,29 +56,14 @@ export type DevServerOptions = {
3256
nodeArgs: string[]
3357
clearScreen?: boolean
3458
env?: NodeJS.ProcessEnv
35-
metaFiles?: {
36-
pattern: string
37-
reloadServer: boolean
38-
}[]
39-
assets?:
40-
| {
41-
serve: false
42-
driver?: string
43-
cmd?: string
44-
}
45-
| {
46-
serve: true
47-
driver: string
48-
cmd: string
49-
}
59+
metaFiles?: MetaFile[]
60+
assets?: AssetsBundlerOptions
5061
}
5162

5263
/**
5364
* Options accepted by the project bundler
5465
*/
5566
export type BundlerOptions = {
56-
metaFiles?: {
57-
pattern: string
58-
reloadServer: boolean
59-
}[]
67+
metaFiles?: MetaFile[]
68+
assets?: AssetsBundlerOptions
6069
}

0 commit comments

Comments
 (0)