6.4 Check or transpile


Once the module graph is ready, it's time to do a check or transpile. As mentioned earlier, the decision comes from the user. If startup speed is important, the type-checking can be skipped in favor of a quick transpilation.
In the previous chapter, only one module was transpiled because there were no dependencies. However, the hello world program v2 has some imports. So, transpilation would be required for all the modules that have Typescript code.
To keep it easy, we'll only go over transpilation, and skip check for this example.


Transpilation needs to be done for all the modules present in the module graph. If the modules don't have TS code, they'd be skipped. Also, if the modules have a valid emit, they'd get skipped unless the reload option is specified. This way startup would get faster.
Let's revisit the code of transpiling a graph:
pub fn transpile(
&mut self,
options: TranspileOptions,
) -> Result<ResultInfo, AnyError> {
let start = Instant::now();
let emit_options: ast::EmitOptions = ts_config.clone().into();
let mut emit_count: u128 = 0;
let config = ts_config.as_bytes();
for (_, module_slot) in self.modules.iter_mut() {
if let ModuleSlot::Module(module) = module_slot {
if module.media_type == MediaType::Dts {
if !(emit_options.check_js
|| module.media_type == MediaType::JSX
|| module.media_type == MediaType::TSX
|| module.media_type == MediaType::TypeScript)
if !options.reload && module.is_emit_valid(&config) {
if module.maybe_parsed_module.is_none() {
let parsed_module = module.maybe_parsed_module.clone().unwrap();
let emit = parsed_module.transpile(&emit_options)?;
emit_count += 1;
module.maybe_emit = Some(Emit::Cli(emit));
module.is_dirty = true;
let stats = Stats(vec![
("Files".to_string(), self.modules.len() as u128),
("Emitted".to_string(), emit_count),
("Total time".to_string(), start.elapsed().as_millis()),
Ok(ResultInfo {
diagnostics: Default::default(),
loadable_modules: self.get_loadable_modules(),
Here are the steps in detail:
  • Prepare TS compilation config
  • Loop through all the modules present in the graph
    • If JS needs to be ignored
      • skip JS files
    • If reload is not specified and emit is valid,
      • skip file
    • Parse module if not already parsed
    • Transpile module
    • Return stats and loadable modules
Let's see the same in a flowchart:

Transpile hello world v2

Now that we understand transpile logic, let's see how it happens for the hello world v2 program.
  • Transpile file:///Users/mayankc/Work/source/deno-vs-nodejs/helloLogV2.ts
  • Transpile https://deno.land/x/doze/mod.ts
  • Transpile https://deno.land/x/machine_id/mod.ts
  • Transpile https://deno.land/x/doze/doze.ts
It is immediately noticeable that the order of transpilation is the same as how these nodes got added to the graph.
  • The dependency of the main module
    • https://deno.land/x/doze/mod.ts
    • https://deno.land/x/machine_id/mod.ts
  • The dependency of https://deno.land/x/doze/mod.ts
    • https://deno.land/x/doze/doze.ts
Once all modules in the graph are transpiled, something called loadable modules gets returned from this step. These are the independent modules. The main module is not considered in this list because it is of course a loadable module. Here is the list of loadable modules after transpilation:
  • https://deno.land/x/machine_id/mod.ts
  • https://deno.land/x/doze/mod.ts
Note that the dependency of https://deno.land/x/doze/mod.ts is not considered as a module by itself at least not in the way the module is used in our code. If doze.ts is directly imported somewhere, then it could become a loadable module.
All the modules have been recursively processed and transpiled. There is no TS code from now on. It's time to get them into v8. Let's go through the registration and instantiation process in the next section.