Skip to content

Commit 5c3cfb1

Browse files
committed
feat: add type-check command
1 parent 343f194 commit 5c3cfb1

File tree

3 files changed

+181
-0
lines changed

3 files changed

+181
-0
lines changed

commands/TypeCheck.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* @adonisjs/assembler
3+
*
4+
* (c) Harminder Virk <[email protected]>
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 { BaseCommand, flags } from '@adonisjs/core/build/standalone'
11+
import { TSCONFIG_FILE_NAME } from '../config/paths'
12+
13+
/**
14+
* TypeCheck project without writing the compiled output to the disk
15+
*/
16+
export default class TypeCheck extends BaseCommand {
17+
public static commandName = 'type-check'
18+
public static description =
19+
'Type check TypeScript source without writing the compiled output on disk'
20+
21+
/**
22+
* Path to the TypeScript project configuration file. Defaults to "tsconfig.json"
23+
*/
24+
@flags.string({
25+
description: 'Path to the TypeScript project configuration file',
26+
})
27+
public tsconfig: string = TSCONFIG_FILE_NAME
28+
29+
/**
30+
* Invoked automatically by ace
31+
*/
32+
public async run() {
33+
const { Compiler } = await import('../src/Compiler')
34+
35+
try {
36+
const compiler = new Compiler(this.application.appRoot, [], false, this.logger, this.tsconfig)
37+
const success = await compiler.typeCheck()
38+
39+
/**
40+
* Set exitCode based upon the typecheck status
41+
*/
42+
if (!success) {
43+
this.exitCode = 1
44+
}
45+
} catch (error) {
46+
this.logger.fatal(error)
47+
this.exitCode = 1
48+
}
49+
}
50+
}

src/Compiler/index.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,31 @@ export class Compiler {
155155
)
156156
}
157157

158+
/**
159+
* Typecheck the project without emit
160+
*/
161+
public async typeCheck(): Promise<boolean> {
162+
const config = this.ts.parseConfig()
163+
if (!config) {
164+
return false
165+
}
166+
167+
this.logger.info('type checking typescript source files')
168+
169+
config.options.noEmit = true
170+
const builder = this.ts.tsCompiler.builder(config)
171+
const { diagnostics } = builder.build()
172+
173+
if (diagnostics.length) {
174+
this.logger.error('typescript compiler errors')
175+
this.ts.renderDiagnostics(diagnostics, builder.host)
176+
return false
177+
}
178+
179+
this.logger.success('built successfully')
180+
return true
181+
}
182+
158183
/**
159184
* Compile project. See [[Compiler.compileForProduction]] for
160185
* production build

test/compiler.spec.ts

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -794,4 +794,110 @@ test.group('Compiler', (group) => {
794794

795795
assert.deepEqual(hasFiles, [true, true, false])
796796
}).timeout(0)
797+
798+
test('typecheck and report typescript errors', async (assert) => {
799+
await fs.add(
800+
'.adonisrc.json',
801+
JSON.stringify({
802+
typescript: true,
803+
metaFiles: ['public/**/*.(js|css)'],
804+
})
805+
)
806+
807+
await fs.add(
808+
'tsconfig.json',
809+
JSON.stringify({
810+
include: ['**/*'],
811+
exclude: ['build'],
812+
compilerOptions: {
813+
rootDir: './',
814+
outDir: 'build/dist',
815+
},
816+
})
817+
)
818+
819+
await fs.add('ace', '')
820+
await fs.add('src/foo.ts', "import path from 'path'")
821+
await fs.add('public/styles/main.css', '')
822+
await fs.add('public/scripts/main.js', '')
823+
824+
const compiler = new Compiler(fs.basePath, [], false, ui.logger)
825+
const isValid = await compiler.typeCheck()
826+
827+
assert.isFalse(isValid)
828+
const hasFiles = await Promise.all(
829+
[
830+
'build/dist/.adonisrc.json',
831+
'build/dist/src/foo.js',
832+
'build/dist/public/styles/main.css',
833+
'build/dist/public/scripts/main.js',
834+
].map((file) => fs.fsExtra.pathExists(join(fs.basePath, file)))
835+
)
836+
837+
assert.deepEqual(hasFiles, [false, false, false, false])
838+
839+
assert.deepEqual(ui.testingRenderer.logs, [
840+
{
841+
message: `${info} type checking typescript source files`,
842+
stream: 'stdout',
843+
},
844+
{
845+
message: `${error} typescript compiler errors`,
846+
stream: 'stderr',
847+
},
848+
])
849+
}).timeout(0)
850+
851+
test('complete successfully when typechecking has no errors', async (assert) => {
852+
await fs.add(
853+
'.adonisrc.json',
854+
JSON.stringify({
855+
typescript: true,
856+
metaFiles: ['public/**/*.(js|css)'],
857+
})
858+
)
859+
860+
await fs.add(
861+
'tsconfig.json',
862+
JSON.stringify({
863+
include: ['**/*'],
864+
exclude: ['build'],
865+
compilerOptions: {
866+
rootDir: './',
867+
outDir: 'build/dist',
868+
},
869+
})
870+
)
871+
872+
await fs.add('ace', '')
873+
await fs.add('src/foo.ts', "import 'path'")
874+
await fs.add('public/styles/main.css', '')
875+
await fs.add('public/scripts/main.js', '')
876+
877+
const compiler = new Compiler(fs.basePath, [], false, ui.logger)
878+
const isValid = await compiler.typeCheck()
879+
880+
assert.isTrue(isValid)
881+
const hasFiles = await Promise.all(
882+
[
883+
'build/dist/.adonisrc.json',
884+
'build/dist/src/foo.js',
885+
'build/dist/public/styles/main.css',
886+
'build/dist/public/scripts/main.js',
887+
].map((file) => fs.fsExtra.pathExists(join(fs.basePath, file)))
888+
)
889+
890+
assert.deepEqual(hasFiles, [false, false, false, false])
891+
892+
assert.deepEqual(ui.testingRenderer.logs, [
893+
{
894+
message: `${info} type checking typescript source files`,
895+
stream: 'stdout',
896+
},
897+
{
898+
message: `${success} built successfully`,
899+
stream: 'stdout',
900+
},
901+
])
902+
}).timeout(0)
797903
})

0 commit comments

Comments
 (0)