6.4 Transpile
Overview
Once the module graph is prepared and structured, the next step involves transpiling the code. To prioritize a quicker startup experience, Deno has constrained this phase to focus solely on transpilation. It's important to note that the 'deno run' command exclusively handles this task without engaging in any type checks.
In the preceding chapter, the transpilation process concentrated on a single module due to the absence of dependencies. In contrast, the hello v2 program encompasses various imports, necessitating the transpilation of all associated modules that contain TypeScript code.
Transpile
Transpilation is a crucial process that must be carried out for every module that is part of the module graph. This ensures that the code written in TypeScript (TS) is converted into a format that can be understood and executed by the V8 engine. It's important to note that modules without any TypeScript code will be excluded from this process, as there would be no need to convert them.
Another aspect to consider is that modules which already have a valid output from a previous transpilation, known as an "emit," will also be skipped during this process. This skipping occurs automatically, unless a specific instruction is given to reload and re-transpile these modules. This optimization strategy is implemented to enhance the startup speed of the application. By omitting unnecessary transpilation for modules with existing valid outputs, the overall time taken to initiate the application is reduced, leading to a faster and more efficient startup experience.
For a quick recap, here is the code for the main transpile function:
pub fn transpile(&self, options: &EmitOptions) -> Result<TranspiledSource> {
let program = (*self.program()).clone();
let source_map = Rc::new(SourceMap::default());
let source_map_config = SourceMapConfig {
inline_sources: options.inline_sources,
};
let file_name = match ModuleSpecifier::parse(self.specifier()) {
Ok(specifier) => FileName::Url(specifier),
Err(_) => FileName::Custom(self.specifier().to_string()),
};
source_map.new_source_file(file_name, self.text_info().text().to_string());
// needs to align with what's done internally in source map
assert_eq!(1, self.text_info().range().start.as_byte_pos().0);
// we need the comments to be mutable, so make it single threaded
let comments = self.comments().as_single_threaded();
let globals = Globals::new();
crate::swc::common::GLOBALS.set(&globals, || {
let top_level_mark = Mark::fresh(Mark::root());
let program = fold_program(
program,
options,
source_map.clone(),
&comments,
top_level_mark,
self.diagnostics(),
)?;
let mut src_map_buf = vec![];
let mut buf = vec![];
{
let mut writer = Box::new(JsWriter::new(
source_map.clone(),
"\n",
&mut buf,
Some(&mut src_map_buf),
));
writer.set_indent_str(" "); // two spaces
let config = crate::swc::codegen::Config {
minify: false,
ascii_only: false,
omit_last_semi: false,
target: ES_VERSION,
};
let mut emitter = crate::swc::codegen::Emitter {
cfg: config,
comments: Some(&comments),
cm: source_map.clone(),
wr: writer,
};
program.emit_with(&mut emitter)?;
}
let mut src = String::from_utf8(buf)?;
let mut map: Option<String> = None;
{
let mut buf = Vec::new();
source_map
.build_source_map_with_config(&src_map_buf, None, source_map_config)
.to_writer(&mut buf)?;
if options.inline_source_map {
src.push_str("//# sourceMappingURL=data:application/json;base64,");
base64::encode_config_buf(
buf,
base64::Config::new(base64::CharacterSet::Standard, true),
&mut src,
);
} else {
map = Some(String::from_utf8(buf)?);
}
}
Ok(TranspiledSource {
text: src,
source_map: map,
})
})
}And to make sure we cover everything comprehensively, let's take a look at the code responsible for traversing the module graph during transpilation:
These are the steps explained in detail:
Set up the TypeScript compilation configuration.
Go through all the modules in the graph.
If JavaScript (JS) should be ignored, then skip JS files.
If reload is not defined and emit is valid, then skip the file.
Parse the module if it hasn't been parsed already.
Convert/transpile the module.
Provide statistics and the list of loadable modules as output.
Let's see the same in a flowchart:

Transpile hello world v2
Now that we understand the transpile logic, let's see how it operates for the hello v2 program. There are two files to transpile:
1. The main application file
2. The Machine_id module
Nanoid is already in JavaScript, so it doesn't require transpilation.
Transpile file:///Users/mayankc/Work/source/denoExamples/helloV2.tsTranspile https://deno.land/x/machine_id@v0.3.0/mod.ts
It's easy to see that the sequence of transpilation follows the order of adding nodes to the graph.
When all modules in the graph are transpiled, the result is a set of modules called "loadable modules." These modules can operate independently. The main module is excluded from this list since it's naturally a loadable module. Here's the list of loadable modules once transpilation is complete:
https://deno.land/x/machine_id@v0.3.0/mod.ts
Output
For the output of transpilation, there are two JS files:
helloV2.ts
machine_id's mod.ts
--
All the modules have been processed and converted, with recursion applied. There's no more TS code from this point. It's time to integrate them into v8. We'll cover the registration and instantiation process in the following section.
Last updated