Skip to content

Commit 2599d3d

Browse files
build: add ts support in core modules
1 parent d080801 commit 2599d3d

File tree

8 files changed

+733
-158
lines changed

8 files changed

+733
-158
lines changed

configure.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1765,7 +1765,8 @@ def gcc_version_ge(version_checked):
17651765
return True
17661766

17671767
def configure_node_lib_files(o):
1768-
o['variables']['node_library_files'] = SearchFiles('lib', 'js')
1768+
o['variables']['node_library_files'] = SearchFiles('lib', 'js') + \
1769+
SearchFiles('lib', 'ts')
17691770

17701771
def configure_node_cctest_sources(o):
17711772
o['variables']['node_cctest_sources'] = [ 'src/node_snapshot_stub.cc' ] + \

node.gyp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1557,12 +1557,27 @@
15571557
'target_name': 'node_js2c',
15581558
'type': 'executable',
15591559
'toolsets': ['host'],
1560+
'dependencies': [
1561+
'tools/v8_gypfiles/v8.gyp:v8_base_without_compiler',
1562+
'tools/v8_gypfiles/v8.gyp:v8_compiler_for_mksnapshot',
1563+
'tools/v8_gypfiles/v8.gyp:v8_init',
1564+
'tools/v8_gypfiles/v8.gyp:v8_snapshot',
1565+
'tools/v8_gypfiles/v8.gyp:v8_libbase',
1566+
'tools/v8_gypfiles/v8.gyp:v8_libplatform',
1567+
'tools/v8_gypfiles/v8.gyp:v8_maybe_icu',
1568+
'tools/v8_gypfiles/v8.gyp:fp16',
1569+
'tools/v8_gypfiles/abseil.gyp:abseil',
1570+
],
15601571
'include_dirs': [
1572+
'deps/v8',
1573+
'deps/v8/include',
15611574
'tools',
15621575
'src',
15631576
],
15641577
'sources': [
15651578
'tools/js2c.cc',
1579+
'tools/typescript_transpiler.cc',
1580+
'tools/typescript_transpiler.h',
15661581
'tools/executable_wrapper.h',
15671582
'src/embedded_data.h',
15681583
'src/embedded_data.cc',

src/node_builtins.cc

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,12 @@ BuiltinLoader::BuiltinCategories BuiltinLoader::GetBuiltinCategories() const {
178178
}
179179

180180
#ifdef NODE_BUILTIN_MODULES_PATH
181-
static std::string OnDiskFileName(const char* id) {
181+
std::string BuiltinLoader::OnDiskFileName(const char* id) const {
182182
std::string filename = NODE_BUILTIN_MODULES_PATH;
183183
filename += "/";
184184

185+
// Save the original id for source map lookup before any pointer arithmetic.
186+
const char* original_id = id;
185187
if (strncmp(id, "internal/deps", strlen("internal/deps")) == 0) {
186188
id += strlen("internal/");
187189
} else {
@@ -192,7 +194,15 @@ static std::string OnDiskFileName(const char* id) {
192194
// V8 tools scripts are .mjs files.
193195
filename += ".mjs";
194196
} else {
195-
filename += ".js";
197+
// Use the pre-built source map to determine the file extension.
198+
// This avoids a filesystem probe on every module load in dev mode.
199+
auto source = source_.read();
200+
auto it = source->find(original_id);
201+
if (it != source->end() && it->second.is_typescript) {
202+
filename += ".ts";
203+
} else {
204+
filename += ".js";
205+
}
196206
}
197207

198208
return filename;

src/node_builtins.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ struct BuiltinSource {
7373
std::string id;
7474
UnionBytes source;
7575
BuiltinSourceType type;
76+
bool is_typescript = false;
7677
};
7778

7879
using BuiltinSourceMap = std::map<std::string, BuiltinSource>;
@@ -203,6 +204,13 @@ class NODE_EXTERN_PRIVATE BuiltinLoader {
203204
const BuiltinSource* AddExternalizedBuiltin(const char* id,
204205
const char* filename);
205206

207+
#ifdef NODE_BUILTIN_MODULES_PATH
208+
// Returns the on-disk path for a builtin module id.
209+
// Uses the pre-built source map to determine the file extension rather
210+
// than probing the filesystem.
211+
std::string OnDiskFileName(const char* id) const;
212+
#endif
213+
206214
ThreadsafeCopyOnWrite<BuiltinSourceMap> source_;
207215

208216
const UnionBytes config_;

tools/js2c.cc

Lines changed: 110 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
#include <map>
99
#include <string>
1010
#include <string_view>
11+
#include <unordered_set>
1112
#include <vector>
1213
#include "builtin_info.h"
1314
#include "embedded_data.h"
1415
#include "executable_wrapper.h"
1516
#include "simdutf.h"
17+
#include "typescript_transpiler.h"
1618
#include "uv.h"
1719

1820
#if defined(_WIN32)
@@ -131,13 +133,14 @@ bool SearchFiles(const std::string& dir,
131133

132134
constexpr std::string_view kMjsSuffix = ".mjs";
133135
constexpr std::string_view kJsSuffix = ".js";
136+
constexpr std::string_view kTsSuffix = ".ts";
134137
constexpr std::string_view kGypiSuffix = ".gypi";
135138
constexpr std::string_view depsPrefix = "deps/";
136139
constexpr std::string_view libPrefix = "lib/";
137140

138141
constexpr std::string_view HasAllowedExtensions(
139142
const std::string_view filename) {
140-
for (const auto& ext : {kGypiSuffix, kJsSuffix, kMjsSuffix}) {
143+
for (const auto& ext : {kGypiSuffix, kJsSuffix, kMjsSuffix, kTsSuffix}) {
141144
if (filename.ends_with(ext)) {
142145
return ext;
143146
}
@@ -329,11 +332,13 @@ std::string GetFileId(const std::string& filename) {
329332
size_t end = filename.size();
330333
size_t start = 0;
331334
std::string prefix;
332-
// Strip .mjs and .js suffix
335+
// Strip .mjs, .js and .ts suffix
333336
if (filename.ends_with(kMjsSuffix)) {
334337
end -= kMjsSuffix.size();
335338
} else if (filename.ends_with(kJsSuffix)) {
336339
end -= kJsSuffix.size();
340+
} else if (filename.ends_with(kTsSuffix)) {
341+
end -= kTsSuffix.size();
337342
}
338343

339344
// deps/acorn/acorn/dist/acorn.js -> internal/deps/acorn/acorn/dist/acorn
@@ -670,6 +675,7 @@ Fragment GetDefinition(const std::string& var, const std::vector<char>& code) {
670675
}
671676

672677
int AddModule(const std::string& filename,
678+
TypeScriptTranspiler* transpiler,
673679
Fragments* definitions,
674680
Fragments* initializers,
675681
Fragments* registrations) {
@@ -684,6 +690,21 @@ int AddModule(const std::string& filename,
684690
if (error != 0) {
685691
return error;
686692
}
693+
694+
if (filename.ends_with(kTsSuffix)) {
695+
std::vector<char> transpiled;
696+
if (transpiler->Strip(std::string_view(code.data(), code.size()),
697+
filename,
698+
&transpiled) != 0) {
699+
fprintf(stderr,
700+
"Failed to transpile TypeScript file %s: %s\n",
701+
filename.c_str(),
702+
std::string(transpiler->LastError()).c_str());
703+
return 1;
704+
}
705+
code = std::move(transpiled);
706+
}
707+
687708
std::string file_id = GetFileId(filename);
688709
std::string var = GetVariableName(file_id);
689710

@@ -696,18 +717,20 @@ int AddModule(const std::string& filename,
696717
// {"internal/deps/v8/tools/tickprocessor-driver",
697718
// BuiltinSource{UnionBytes(&fs_resource),
698719
// BuiltinSourceType::kSourceTextModule}},
699-
Fragment& init_buf = initializers->emplace_back(Fragment(512, 0));
720+
Fragment& init_buf = initializers->emplace_back(Fragment(640, 0));
700721
int init_size = snprintf(init_buf.data(),
701722
init_buf.size(),
702723
" {\"%s\","
703724
" BuiltinSource{"
704725
" \"%s\","
705726
" UnionBytes(&%s_resource),"
706-
" BuiltinSourceType::%s} },",
727+
" BuiltinSourceType::%s,"
728+
" %s} },",
707729
file_id.c_str(),
708730
file_id.c_str(),
709731
var.c_str(),
710-
source_type.c_str());
732+
source_type.c_str(),
733+
filename.ends_with(kTsSuffix) ? "true" : "false");
711734
init_buf.resize(init_size);
712735

713736
// Registrations:
@@ -826,23 +849,39 @@ int AddGypi(const std::string& var,
826849

827850
int JS2C(const FileList& js_files,
828851
const FileList& mjs_files,
852+
const FileList& ts_files,
829853
const std::string& config,
854+
const char* argv0,
830855
const std::string& dest) {
856+
TypeScriptTranspiler transpiler;
831857
Fragments definitions;
832-
definitions.reserve(js_files.size() + mjs_files.size() + 1);
858+
definitions.reserve(js_files.size() + mjs_files.size() + ts_files.size() + 1);
833859
Fragments initializers;
834-
initializers.reserve(js_files.size() + mjs_files.size());
860+
initializers.reserve(js_files.size() + mjs_files.size() + ts_files.size());
835861
Fragments registrations;
836-
registrations.reserve(js_files.size() + mjs_files.size() + 1);
862+
registrations.reserve(js_files.size() + mjs_files.size() + ts_files.size() +
863+
1);
837864

838-
for (const auto& filename : js_files) {
839-
int r = AddModule(filename, &definitions, &initializers, &registrations);
840-
if (r != 0) {
841-
return r;
842-
}
865+
if (!ts_files.empty() && transpiler.Initialize(argv0) != 0) {
866+
fprintf(stderr,
867+
"Failed to initialize TypeScript transpiler: %s\n",
868+
std::string(transpiler.LastError()).c_str());
869+
return 1;
843870
}
844-
for (const auto& filename : mjs_files) {
845-
int r = AddModule(filename, &definitions, &initializers, &registrations);
871+
872+
auto add_modules = [&](const FileList& files) {
873+
for (const auto& filename : files) {
874+
int r = AddModule(
875+
filename, &transpiler, &definitions, &initializers, &registrations);
876+
if (r != 0) {
877+
return r;
878+
}
879+
}
880+
return 0;
881+
};
882+
883+
for (const auto* files : {&js_files, &mjs_files, &ts_files}) {
884+
int r = add_modules(*files);
846885
if (r != 0) {
847886
return r;
848887
}
@@ -910,7 +949,8 @@ int Main(int argc, char* argv[]) {
910949
const std::string& file = args[i];
911950
if (IsDirectory(file, &error)) {
912951
if (!SearchFiles(file, &file_map, kJsSuffix) ||
913-
!SearchFiles(file, &file_map, kMjsSuffix)) {
952+
!SearchFiles(file, &file_map, kMjsSuffix) ||
953+
!SearchFiles(file, &file_map, kTsSuffix)) {
914954
return 1;
915955
}
916956
} else if (error != 0) {
@@ -927,8 +967,15 @@ int Main(int argc, char* argv[]) {
927967
}
928968
}
929969

930-
// Should have exactly 3 types: `.js`, `.mjs` and `.gypi`.
931-
assert(file_map.size() == 3);
970+
// Should have at most 4 extension types: `.js`, `.mjs`, `.ts` and `.gypi`.
971+
// Any other extension indicates an unexpected file slipped into the inputs.
972+
if (file_map.size() > 4) {
973+
fprintf(stderr,
974+
"Unexpected file types in inputs (expected .js, .mjs, .ts, "
975+
".gypi only)\n");
976+
return 1;
977+
}
978+
932979
auto gypi_it = file_map.find(".gypi");
933980
// Currently config.gypi is the only `.gypi` file allowed
934981
if (gypi_it == file_map.end() || gypi_it->second.size() != 1 ||
@@ -940,19 +987,55 @@ int Main(int argc, char* argv[]) {
940987
}
941988
auto js_it = file_map.find(".js");
942989
auto mjs_it = file_map.find(".mjs");
943-
assert(js_it != file_map.end() && mjs_it != file_map.end());
990+
auto ts_it = file_map.find(".ts");
991+
if (mjs_it != file_map.end()) {
992+
auto it = std::find(mjs_it->second.begin(),
993+
mjs_it->second.end(),
994+
"lib/eslint.config_partial.mjs");
995+
if (it != mjs_it->second.end()) {
996+
mjs_it->second.erase(it);
997+
}
998+
}
944999

945-
auto it = std::find(mjs_it->second.begin(),
946-
mjs_it->second.end(),
947-
"lib/eslint.config_partial.mjs");
948-
if (it != mjs_it->second.end()) {
949-
mjs_it->second.erase(it);
1000+
if (js_it != file_map.end()) {
1001+
std::sort(js_it->second.begin(), js_it->second.end());
1002+
}
1003+
if (mjs_it != file_map.end()) {
1004+
std::sort(mjs_it->second.begin(), mjs_it->second.end());
1005+
}
1006+
if (ts_it != file_map.end()) {
1007+
std::sort(ts_it->second.begin(), ts_it->second.end());
9501008
}
9511009

952-
std::sort(js_it->second.begin(), js_it->second.end());
953-
std::sort(mjs_it->second.begin(), mjs_it->second.end());
1010+
static const FileList empty_list;
1011+
const FileList& js_files =
1012+
js_it == file_map.end() ? empty_list : js_it->second;
1013+
const FileList& mjs_files =
1014+
mjs_it == file_map.end() ? empty_list : mjs_it->second;
1015+
const FileList& ts_files =
1016+
ts_it == file_map.end() ? empty_list : ts_it->second;
1017+
1018+
// Detect duplicate module IDs across .js/.mjs and .ts file lists.
1019+
// GetFileId strips the extension, so lib/foo.ts and lib/foo.js would both
1020+
// resolve to the same ID "foo", causing a silent registration conflict.
1021+
if (!ts_files.empty()) {
1022+
std::unordered_set<std::string> known_ids;
1023+
for (const auto& f : js_files) known_ids.insert(GetFileId(f));
1024+
for (const auto& f : mjs_files) known_ids.insert(GetFileId(f));
1025+
for (const auto& f : ts_files) {
1026+
std::string id = GetFileId(f);
1027+
if (known_ids.count(id) != 0) {
1028+
fprintf(stderr,
1029+
"Duplicate module ID '%s': a .ts file and a .js/.mjs file "
1030+
"both resolve to the same module ID\n",
1031+
id.c_str());
1032+
return 1;
1033+
}
1034+
}
1035+
}
9541036

955-
return JS2C(js_it->second, mjs_it->second, gypi_it->second[0], output);
1037+
return JS2C(
1038+
js_files, mjs_files, ts_files, gypi_it->second[0], argv[0], output);
9561039
}
9571040
} // namespace js2c
9581041
} // namespace node

0 commit comments

Comments
 (0)