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:
pubfntranspile(&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 =matchModuleSpecifier::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 mapassert_eq!(1, self.text_info().range().start.as_byte_pos().0);// we need the comments to be mutable, so make it single threadedlet 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(), )?;letmut src_map_buf =vec![];letmut buf =vec![]; {letmut writer = Box::new(JsWriter::new( source_map.clone(),"\n",&mut buf,Some(&mut src_map_buf), )); writer.set_indent_str(" "); // two spaceslet config =crate::swc::codegen::Config { minify:false, ascii_only:false, omit_last_semi:false, target: ES_VERSION, };letmut emitter =crate::swc::codegen::Emitter { cfg: config, comments:Some(&comments), cm: source_map.clone(), wr: writer, }; program.emit_with(&mut emitter)?; }letmut src =String::from_utf8(buf)?;letmut map:Option<String> =None; {letmut 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:
pubfncache_module_emits(&self, graph:&ModuleGraph, ) ->Result<(), AnyError> {for module in graph.modules() {ifletModule::Esm(module) = module {let is_emittable =matches!( module.media_type,MediaType::TypeScript|MediaType::Mts|MediaType::Cts|MediaType::Jsx|MediaType::Tsx );if is_emittable { self.emit_parsed_source(&module.specifier, module.media_type,&module.source, )?; } } }Ok(())}pubfnemit_parsed_source(&self, specifier:&ModuleSpecifier, media_type:MediaType, source:&Arc<str>, ) ->Result<ModuleCode, AnyError> {let source_hash = self.get_source_hash(source);ifletSome(emit_code) = self.emit_cache.get_emit_code(specifier, source_hash) {Ok(emit_code.into()) } else {// this will use a cached version if it existslet parsed_source = self.parsed_source_cache.get_or_parse_module( specifier, source.clone(), media_type, )?;let transpiled_source = parsed_source.transpile(&self.emit_options)?;debug_assert!(transpiled_source.source_map.is_none()); self.emit_cache.set_emit_code( specifier, source_hash,&transpiled_source.text, );Ok(transpiled_source.text.into()) } }
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.
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:
const { run,build,readAll,readFile,env } = Deno;// Get machine ID// Permission in Windows: --allow-run --allow-env// Permission in MacOS: --allow-run// Permission in Linux: --allow-readexportasyncfunctiongetMachineId():Promise<string> {switch (build.os) {case"linux":returngetMachineIDLinux();case"windows":returngetMachineIDWin();case"darwin":returngetMachineIDMac();default:thrownewError(`Not support your operate system '${build.os}'`); }}functionparse(bytes:Uint8Array):string {constoutput=newTextDecoder().decode(bytes);switch (build.os) {case"linux":returnoutput.trim();case"windows":return output.toString().split("REG_SZ")[1].replace(/\r+|\n+|\s+/gi,"").trim();case"darwin":constlines=output.split("\n");for (constlineof lines) {// here is the match line// "IOPlatformUUID" = "A8226C69-2364-5B3E-83CC-1A72D7531679"if (line.indexOf("IOPlatformUUID") >0) {const [_,val] =line.split(/\s*=\s*/);returnval.replace(/^"|"$/g,""); } }return"";default:thrownewError(`Not support your operate system '${build.os}'`); }}asyncfunctiongetMachineIDWin():Promise<string> {constwinDir=env.get("windir");constps=run({ stdout:"piped", cmd: [`${winDir}\\System32\\REG.exe`,"QUERY","HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography","/v","MachineGuid", ], });constoutput=awaitreadAll(ps.stdout!);ps.stdout?.close();ps.close();returnparse(output);}asyncfunctiongetMachineIDMac():Promise<string> {constps=run({ stdout:"piped", cmd: ["ioreg","-rd1","-c","IOPlatformExpertDevice"], });constoutput=awaitreadAll(ps.stdout!);ps.stdout?.close();ps.close();returnparse(output);}asyncfunctiongetMachineIDLinux():Promise<string> {// dbusPath is the default path for dbus machine id.constdbusPath="/var/lib/dbus/machine-id";// dbusPathEtc is the default path for dbus machine id located in /etc.// Some systems (like Fedora 20) only know this path.// Sometimes it's the other way round.constdbusPathEtc="/etc/machine-id";returnparse(awaitreadFile(dbusPath).catch(() => {// try fallback pathreturnreadFile(dbusPathEtc); }), );}
--
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.