Skip to content

Commit e74297f

Browse files
committed
Provide compiled JSON to TypeScript compiler.
Fixes denoland#4101 Previously, we would just provide the raw JSON to the TypeScript compiler worker, but TypeScript does not transform JSON. This caused a problem when emitting a bundle, that the JSON would just be "inlined" into the output, instead of being transformed into a module. This fixes this problem by providing the compiled JSON to the TypeScript compiler, so TypeScript just sees JSON as a "normal" TypeScript module.
1 parent f9557a4 commit e74297f

File tree

5 files changed

+60
-7
lines changed

5 files changed

+60
-7
lines changed

cli/js/compiler/host.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,16 @@ export class Host implements ts.CompilerHost {
242242
assert(sourceFile != null);
243243
if (!sourceFile.tsSourceFile) {
244244
assert(sourceFile.sourceCode != null);
245+
// even though we assert the extension for JSON modules to the compiler
246+
// is TypeScript, TypeScript internally analyses the filename for its
247+
// extension and tries to parse it as JSON instead of TS. We have to
248+
// change the filename to the TypeScript file.
245249
sourceFile.tsSourceFile = ts.createSourceFile(
246-
fileName.startsWith(ASSETS) ? sourceFile.filename : fileName,
250+
fileName.startsWith(ASSETS)
251+
? sourceFile.filename
252+
: fileName.toLowerCase().endsWith(".json")
253+
? `${fileName}.ts`
254+
: fileName,
247255
sourceFile.sourceCode,
248256
languageVersion
249257
);

cli/js/compiler/sourcefile.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ function getExtension(fileName: string, mediaType: MediaType): ts.Extension {
3535
case MediaType.TSX:
3636
return ts.Extension.Tsx;
3737
case MediaType.Json:
38-
return ts.Extension.Json;
38+
// we internally compile JSON, so what gets provided to the TypeScript
39+
// compiler is an ES module, but in order to get TypeScript to handle it
40+
// properly we have to pretend it is TS.
41+
return ts.Extension.Ts;
3942
case MediaType.Wasm:
4043
// Custom marker for Wasm type.
4144
return ts.Extension.Js;

cli/js/compiler/util.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { bold, cyan, yellow } from "../colors.ts";
44
import { CompilerOptions } from "./api.ts";
55
import { buildBundle } from "./bundler.ts";
66
import { ConfigureResponse, Host } from "./host.ts";
7-
import { SourceFile } from "./sourcefile.ts";
7+
import { MediaType, SourceFile } from "./sourcefile.ts";
88
import { atob, TextEncoder } from "../web/text_encoding.ts";
99
import * as compilerOps from "../ops/compiler.ts";
1010
import * as util from "../util.ts";
@@ -51,13 +51,13 @@ function cache(
5151
// NOTE: If it's a `.json` file we don't want to write it to disk.
5252
// JSON files are loaded and used by TS compiler to check types, but we don't want
5353
// to emit them to disk because output file is the same as input file.
54-
if (sf.extension === ts.Extension.Json) {
54+
if (sf.mediaType === MediaType.Json) {
5555
return;
5656
}
5757

5858
// NOTE: JavaScript files are only cached to disk if `checkJs`
5959
// option in on
60-
if (sf.extension === ts.Extension.Js && !checkJs) {
60+
if (sf.mediaType === MediaType.JavaScript && !checkJs) {
6161
return;
6262
}
6363
}

cli/ops/compiler.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,9 @@ fn op_fetch_source_files(
136136
}
137137
_ => f,
138138
};
139-
// Special handling of Wasm files:
139+
// Special handling of WASM and JSON files:
140140
// compile them into JS first!
141-
// This allows TS to do correct export types.
141+
// This allows TS to do correct export types as well as bundles.
142142
let source_code = match file.media_type {
143143
msg::MediaType::Wasm => {
144144
global_state
@@ -148,6 +148,14 @@ fn op_fetch_source_files(
148148
.map_err(|e| OpError::other(e.to_string()))?
149149
.code
150150
}
151+
msg::MediaType::Json => {
152+
global_state
153+
.json_compiler
154+
.compile(&file)
155+
.await
156+
.map_err(|e| OpError::other(e.to_string()))?
157+
.code
158+
}
151159
_ => String::from_utf8(file.source_code).unwrap(),
152160
};
153161
Ok::<_, OpError>(json!({

cli/tests/integration_tests.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,40 @@ fn bundle_single_module() {
397397
assert_eq!(output.stderr, b"");
398398
}
399399

400+
#[test]
401+
fn bundle_json() {
402+
use tempfile::TempDir;
403+
404+
let json_modules = util::root_path().join("cli/tests/020_json_modules.ts");
405+
assert!(json_modules.is_file());
406+
let t = TempDir::new().expect("tempdir fail");
407+
let bundle = t.path().join("020_json_modules.bundle.js");
408+
let mut deno = util::deno_cmd()
409+
.current_dir(util::root_path())
410+
.arg("bundle")
411+
.arg(json_modules)
412+
.arg(&bundle)
413+
.spawn()
414+
.expect("failed to spawn script");
415+
let status = deno.wait().expect("failed to wait for the child process");
416+
assert!(status.success());
417+
assert!(bundle.is_file());
418+
419+
let output = util::deno_cmd()
420+
.current_dir(util::root_path())
421+
.arg("run")
422+
.arg("--reload")
423+
.arg(&bundle)
424+
.output()
425+
.expect("failed to spawn script");
426+
// check the output of the the bundle program.
427+
assert!(std::str::from_utf8(&output.stdout)
428+
.unwrap()
429+
.trim()
430+
.ends_with("{\"foo\":{\"bar\":true,\"baz\":[\"qat\",1]}}"));
431+
assert_eq!(output.stderr, b"");
432+
}
433+
400434
#[test]
401435
fn bundle_tla() {
402436
// First we have to generate a bundle of some module that has exports.

0 commit comments

Comments
 (0)